Showing preview only (404K chars total). Download the full file or copy to clipboard to get everything.
Repository: tebru/retrofit-php
Branch: master
Commit: dbd1c4a2cf8c
Files: 164
Total size: 362.3 KB
Directory structure:
gitextract_syq3xf3v/
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── CONTRIBUTORS.md
├── LICENSE.md
├── README.md
├── bin/
│ └── retrofit
├── composer.json
├── docs/
│ ├── advanced_usage.md
│ ├── annotations.md
│ ├── installation.md
│ ├── upgrade_2_3.md
│ └── usage.md
├── phpcs.xml
├── phpunit.xml
├── src/
│ ├── Annotation/
│ │ ├── Body.php
│ │ ├── DELETE.php
│ │ ├── Encodable.php
│ │ ├── ErrorBody.php
│ │ ├── Field.php
│ │ ├── FieldMap.php
│ │ ├── GET.php
│ │ ├── HEAD.php
│ │ ├── Header.php
│ │ ├── HeaderMap.php
│ │ ├── Headers.php
│ │ ├── HttpRequest.php
│ │ ├── OPTIONS.php
│ │ ├── PATCH.php
│ │ ├── POST.php
│ │ ├── PUT.php
│ │ ├── ParameterAnnotation.php
│ │ ├── ParameterAwareAnnotation.php
│ │ ├── Part.php
│ │ ├── PartMap.php
│ │ ├── Path.php
│ │ ├── Query.php
│ │ ├── QueryMap.php
│ │ ├── QueryName.php
│ │ ├── REQUEST.php
│ │ ├── ResponseBody.php
│ │ └── Url.php
│ ├── AnnotationHandler.php
│ ├── Call.php
│ ├── CallAdapter.php
│ ├── CallAdapterFactory.php
│ ├── Command/
│ │ └── CompileCommand.php
│ ├── Converter.php
│ ├── ConverterFactory.php
│ ├── DefaultProxyFactoryAware.php
│ ├── Exception/
│ │ └── ResponseHandlingFailedException.php
│ ├── Finder/
│ │ └── ServiceResolver.php
│ ├── Http/
│ │ └── MultipartBody.php
│ ├── HttpClient.php
│ ├── Internal/
│ │ ├── AnnotationHandler/
│ │ │ ├── BodyAnnotHandler.php
│ │ │ ├── FieldAnnotHandler.php
│ │ │ ├── FieldMapAnnotHandler.php
│ │ │ ├── HeaderAnnotHandler.php
│ │ │ ├── HeaderMapAnnotHandler.php
│ │ │ ├── HeadersAnnotHandler.php
│ │ │ ├── HttpRequestAnnotHandler.php
│ │ │ ├── PartAnnotHandler.php
│ │ │ ├── PartMapAnnotHandler.php
│ │ │ ├── PathAnnotHandler.php
│ │ │ ├── QueryAnnotHandler.php
│ │ │ ├── QueryMapAnnotHandler.php
│ │ │ ├── QueryNameAnnotHandler.php
│ │ │ └── UrlAnnotHandler.php
│ │ ├── AnnotationProcessor.php
│ │ ├── CacheProvider.php
│ │ ├── CallAdapter/
│ │ │ ├── CallAdapterProvider.php
│ │ │ ├── DefaultCallAdapter.php
│ │ │ └── DefaultCallAdapterFactory.php
│ │ ├── Converter/
│ │ │ ├── ConverterProvider.php
│ │ │ ├── DefaultConverterFactory.php
│ │ │ ├── DefaultRequestBodyConverter.php
│ │ │ ├── DefaultResponseBodyConverter.php
│ │ │ ├── DefaultStringConverter.php
│ │ │ └── NoopStringConverter.php
│ │ ├── DefaultProxyFactory.php
│ │ ├── Filesystem.php
│ │ ├── HttpClientCall.php
│ │ ├── ParameterHandler/
│ │ │ ├── AbstractParameterHandler.php
│ │ │ ├── BodyParamHandler.php
│ │ │ ├── FieldMapParamHandler.php
│ │ │ ├── FieldParamHandler.php
│ │ │ ├── HeaderMapParamHandler.php
│ │ │ ├── HeaderParamHandler.php
│ │ │ ├── PartMapParamHandler.php
│ │ │ ├── PartParamHandler.php
│ │ │ ├── PathParamHandler.php
│ │ │ ├── QueryMapParamHandler.php
│ │ │ ├── QueryNameParamHandler.php
│ │ │ ├── QueryParamHandler.php
│ │ │ └── UrlParamHandler.php
│ │ ├── RequestBuilder.php
│ │ ├── RetrofitResponse.php
│ │ ├── ServiceMethod/
│ │ │ ├── DefaultServiceMethod.php
│ │ │ ├── DefaultServiceMethodBuilder.php
│ │ │ └── ServiceMethodFactory.php
│ │ └── ServiceMethod.php
│ ├── ParameterHandler.php
│ ├── Proxy/
│ │ └── AbstractProxy.php
│ ├── Proxy.php
│ ├── ProxyFactory.php
│ ├── RequestBodyConverter.php
│ ├── Response.php
│ ├── ResponseBodyConverter.php
│ ├── Retrofit.php
│ ├── RetrofitBuilder.php
│ ├── ServiceMethodBuilder.php
│ └── StringConverter.php
└── tests/
├── Mock/
│ └── Unit/
│ ├── Internal/
│ │ ├── AnnotationProcessorTest/
│ │ │ ├── AnnotationProcessorTestMock.php
│ │ │ └── BadConverterAnnotation.php
│ │ ├── HttpClientCallTest/
│ │ │ ├── HttpClientCallTestClientMock.php
│ │ │ ├── HttpClientCallTestErrorBodyMock.php
│ │ │ ├── HttpClientCallTestResponseBodyMock.php
│ │ │ └── HttpClientCallTestServiceMethodMock.php
│ │ ├── ProxyFactoryTest/
│ │ │ ├── PFTCTestCreate.php
│ │ │ ├── PFTCTestCreateCacheDirectoryFail.php
│ │ │ ├── PFTCTestCreateClientFail.php
│ │ │ ├── PFTCTestCreateTwice.php
│ │ │ ├── PFTCTestCreateWithoutCache.php
│ │ │ ├── ProxyFactoryTestClientNoReturnType.php
│ │ │ ├── ProxyFactoryTestClientNoTypehint.php
│ │ │ ├── ProxyFactoryTestFilesystem.php
│ │ │ └── ProxyFactoryTestHttpClient.php
│ │ └── ServiceMethod/
│ │ └── ServiceMethodFactoryTest/
│ │ ├── ServiceMethodFactoryTestClient.php
│ │ └── ServiceMethodFactoryTestConverterFactory.php
│ ├── MockCall.php
│ ├── MockConverterFactory.php
│ └── RetrofitTest/
│ ├── ApiClient.php
│ ├── CacheableApiClient.php
│ ├── DefaultParamsApiClient.php
│ ├── InvalidSyntaxApiClient.php
│ ├── RetrofitTestAdaptedCallMock.php
│ ├── RetrofitTestCallAdapterFactory.php
│ ├── RetrofitTestCallAdapterMock.php
│ ├── RetrofitTestConverterFactory.php
│ ├── RetrofitTestCustomAnnotation.php
│ ├── RetrofitTestCustomAnnotationHandler.php
│ ├── RetrofitTestDelegateProxy.php
│ ├── RetrofitTestHttpClient.php
│ ├── RetrofitTestProxyFactory.php
│ ├── RetrofitTestRequestBodyConverter.php
│ ├── RetrofitTestRequestBodyMock.php
│ ├── RetrofitTestResponseBodyConverter.php
│ └── RetrofitTestResponseBodyMock.php
├── Unit/
│ ├── Internal/
│ │ ├── AnnotationHandlersTest.php
│ │ ├── AnnotationProcessorTest.php
│ │ ├── CallAdapterTest.php
│ │ ├── ConverterTest.php
│ │ ├── DefaultProxyFactoryTest.php
│ │ ├── FilesystemTest.php
│ │ ├── HttpClientCallTest.php
│ │ ├── ParameterHandlersTest.php
│ │ ├── RequestBuilderTest.php
│ │ ├── RetrofitResponseTest.php
│ │ └── ServiceMethod/
│ │ ├── DefaultServiceMethodBuilderTest.php
│ │ ├── ServiceMethodFactoryTest.php
│ │ └── ServiceMethodTest.php
│ └── RetrofitTest.php
└── bootstrap.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
/vendor
/composer.lock
/tests/cache
/build
/.php_cs.cache
================================================
FILE: .travis.yml
================================================
sudo: false
language: php
php:
- 7.1
- 7.2
- 7.3
- 7.4
env:
- SF_CACHE=3
- SF_CACHE=4
- SF_CACHE=4.3
- SF_CACHE=5
jobs:
exclude:
- php: 7.1
env:
- SF_CACHE=5
# remove composer.lock (it should not be published)
before_script:
- composer install --no-interaction -o
- if [ "$SF_CACHE" = "3" ]; then composer require symfony/cache:^3.3; fi;
- if [ "$SF_CACHE" = "4" ]; then composer require symfony/cache:">=4.0 <4.3"; fi;
- if [ "$SF_CACHE" = "4.3" ]; then composer require symfony/cache:^4.3; fi;
- if [ "$SF_CACHE" = "5" ]; then composer require symfony/cache:5.0; fi;
script:
- mkdir -p build/logs
- vendor/bin/phpunit
================================================
FILE: CHANGELOG.md
================================================
Change Log
==========
This document keeps track of important changes between releases of the library.
3.2.0
-----
* ADDED: Exception handling on response body conversion (Should not be a
BC break unless applications are checking for specific exceptions)
3.1.0
-----
* ADDED: Ability to set custom cache adapter
* CHANGED: Default cache to use php file cache over filesystem cache
3.0.3
-----
* FIXED: Issue with default parameters and nulls
3.0.2
-----
* ADDED: Symfony 4 support
3.0.1
-----
* FIXED: Issue with query encoding
3.0.0
-----
* See [upgrade guide](docs/upgrade_2_3.md)
2.9.0
-----
* Dependency updates
* Added request/response to return event
2.8.3
-----
* Fixed issue with AfterSendEvent
2.8.2
-----
* Fixed issue with events
2.8.1
-----
* Updating dependencies
2.8.0
-----
* Added option to return complete response instead of body
* Removed logging of which events occurred
* Dependency updates
* Added multipart support
2.7.0
-----
* Updated dynamo library
2.6.1
-----
* Fixed issue with urlencoding parameters
2.6.0
-----
* Changing client adapter to only accept request interface
* Updating client dependency
2.5.5
-----
* Updating Dynamo library
* Updating minimum dependencies
* Added ability to set a client adapter
2.5.4
-----
* Event updates: adding request
2.5.3
-----
* Fixed bug AfterSendEvent
2.5.2
-----
* Fixed bug in event system
2.5.1
-----
* Fixed issue with urlencode
2.5.0
-----
* Added logging
* Fixed Dyanmo minimum version dependency
2.4.0
-----
* Added support for async requests
* Using http_build_query() to build all query strings
* Allowing modification of return data through event
2.3.0
-----
* Fixed events to dispatch objects that can be modified
2.2.0
-----
* Allowed body object to be form encoded
* Added support for serializing \JsonSerializable
* Fixed issue with booleans getting cast to ints
* Fixed issue with serializing null body objects
* Improved generated code to exclude serialization contexts if not applicable
2.1.0
-----
* Added an event dispatcher
* Added a library specific exception
2.0.0
-----
* Removed composer wrapper
* Removed guzzle dependency (added support for either v5 or v6)
* Abstracted class generation into tebru/dynamo
* Fixed issue with query map
* Fixed issue with interface inheritance
1.2.1
-----
* Upgraded twig to resolve security vulnerability
1.2.0
-----
* Added annotation for JMS serialization contexts
1.1.0
-----
* Added support for JMS serialization contexts
1.0.0
-----
* Mirrored functionality of Square's Retrofit library.
* PSR autoloading of generated classes
* Created binary tool for managing cache.
================================================
FILE: CONTRIBUTING.md
================================================
Contributing Guidelines
=======================
Before contributing code, please be aware of the following:
Code Style
----------
This project uses the [PSR-2] code style. Please follow these formatting
standards where possible.
[PSR-2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
================================================
FILE: CONTRIBUTORS.md
================================================
Contributors
============
This is a credits file of people that have contributed Retrofit-PHP Library.
* Nate Brunette
* Email: n@tebru.net
* Maxwell Vandervelde
* Email: Max@MaxVandervelde.com
* PGP: `4096R/2B95C590 C21D 81E7 1F58 6C96 955F 5141 444C 4178 2B95 C590`
* Matthew Loberg
* Email: loberg.matt@gmail.com
* Edward Pfremmer
* Email: edward.pfremmer@nerdery.com
* Tony Nelson
* Email: tonynelson19@gmail.com
================================================
FILE: LICENSE.md
================================================
Project License
===============
The MIT License (MIT)
Copyright (c) 2015 Nate Brunette
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
Retrofit PHP
============
[](https://travis-ci.org/tebru/retrofit-php)
[](https://scrutinizer-ci.com/g/tebru/retrofit-php/?branch=master)
[](https://scrutinizer-ci.com/g/tebru/retrofit-php/?branch=master)
[](https://insight.sensiolabs.com/projects/d2188bf8-8248-4df6-8bc5-8150fc0b8898)
Retrofit is a type-safe REST client. It is blatantly stolen from
[square/retrofit](https://github.com/square/retrofit) and implemented in
PHP.
❗UPGRADE NOTICE❗
----------------
**Version 3 introduces many breaking changes. Please review the
[upgrade guide](docs/upgrade_2_3.md) before upgrading.**
Overview
--------
*The following is for version 3, please check out the corresponding tag
for version 2 documentation*
Retrofit allows you to define your REST API with a simple interface. The
follow example will attempt to display a typical use-case, but requires
two additional libraries. The first uses Guzzle to make http requests as
Retrofit does not ship with any default way to make network requests. The
second uses a serializer (Gson) to hook into Retrofit's Converter
functionality. This allows for automatic serialization of request bodies
and deserialization of response bodies.
```php
interface GitHubService
{
/**
* @GET("/users/{user}/list")
* @Path("user")
* @ResponseBody("App\GithubService\ListRepo")
* @ErrorBody("App\GitHubService\ApiError")
*/
public function listRepos(string $user): Call;
}
```
Annotations are used to configure the endpoint.
Then, the `Retrofit` class generates a working implementation of the
service interface.
```php
$retrofit = Retrofit::builder()
->setBaseUrl('https://api.github.com')
->setHttpClient(new Guzzle6HttpClient(new Client())) // requires a separate library
->addConverterFactory(new GsonConverterFactory(Gson::builder()->build())) // requies a separate library
->build();
$gitHubService = $retrofit->create(GitHubService::class);
```
Our newly created service is capable of making GET requests to
/users/{user}/list, which returns a `Call` object.
```php
$call = $gitHubService->listRepos('octocat');
```
The `Call` object is then used to execute the request synchronously
or asynchronously, returning a response.
```php
$response = $call->execute();
// or
$call->enqueue(
function(Response $response) { }, // response callback (optional)
function(Throwable $throwable) { } // error callback (optional)
);
$call->wait();
```
You can then check to see if the request was successful and get the
deserialized response body.
```php
if (!$response->isSuccessful()) {
throw new ApiException($response->errorBody());
}
$responseBody = $response->body();
```
*Usage examples are referenced from Square's documentation*
Installation & Usage
--------------------
*Retrofit 3 requires PHP 7.1*
```bash
composer require tebru/retrofit-php
```
Please make sure you also install an http client.
```bash
composer require tebru/retrofit-php-http-guzzle6
```
Install a converter to handle more advanced request and response body
conversions.
```bash
composer require tebru/retrofit-php-converter-gson
```
### Documentation
- [Installation](docs/installation.md)
- [Getting Started](docs/usage.md)
- [Advanced Usage](docs/advanced_usage.md)
- [Annotation Reference](docs/annotations.md)
License
-------
This project is licensed under the MIT license. Please see the `LICENSE` file
for more information.
================================================
FILE: bin/retrofit
================================================
#!/usr/bin/env php
<?php
use Doctrine\Common\Annotations\AnnotationRegistry;
use Tebru\Retrofit\Command\CompileCommand;
use Symfony\Component\Console\Application;
$dirs = [__DIR__ . '/../vendor', __DIR__ . '/../../..'];
foreach ($dirs as $dir) {
$file = $dir . '/autoload.php';
if (file_exists($file)) {
$vendorDir = $dir;
require $file;
}
}
$loader = require $vendorDir . '/autoload.php';
AnnotationRegistry::registerLoader([$loader, 'loadClass']);
$application = new Application();
$application->add(new CompileCommand);
$application->run();
================================================
FILE: composer.json
================================================
{
"name": "tebru/retrofit-php",
"description": "Retrofit for PHP - A type-safe PHP REST client.",
"require": {
"php": ">= 7.1",
"guzzlehttp/psr7": "^1.0",
"nikic/php-parser": "~3.0.6|^4.0",
"symfony/cache": "^3.3|^4.0|^5.0",
"symfony/console": "^3.0|^4.0|^5.0",
"tebru/doctrine-annotation-reader": "^0.3.0",
"tebru/php-type": "^0.1.1"
},
"require-dev": {
"phpunit/phpunit": "^7.3",
"mikey179/vfsstream": "^1.6"
},
"license": "MIT",
"authors": [
{
"name": "Nate Brunette",
"email": "n@tebru.net"
}
],
"autoload": {
"psr-4": {
"Tebru\\Retrofit\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tebru\\Retrofit\\Test\\": "tests/"
}
},
"suggest": {
"guzzlehttp/guzzle": "Required to make requests"
},
"bin": ["bin/retrofit"]
}
================================================
FILE: docs/advanced_usage.md
================================================
Advanced Usage
==============
Caching
-------
By default, Retrofit doesn't do any filesystem caching. In production,
you should enable caching to increase performance.
```php
Retrofit::builder()
->setCacheDir(__DIR__.'/cache')
->enableCache();
```
Custom Converters
-----------------
By default, Retrofit can convert any type to a string, but only handles
PSR-7 `StreamInterface` for request and response bodies. Use a custom
converter to allow handling different types.
```php
Retrofit::builder()
->addConverterFactory(new CustomConverterFactory());
```
This should implement `Tebru\Retrofit\ConverterFactory` with three
methods to convert to string, to a stream for a request body, or from a
stream for a response body. Return null from any of them will cause
Retrofit to skip that converter and move on to the next one.
Each method will receive a `TypeToken`, which you can use to determine
the type of the parameter you're converting.
Currently only a Gson converter exists to handle more complex
conversions.
```bash
composer require tebru/retrofit-php-converter-gson
```
Custom Http Client
------------------
Retrofit doesn't provide any way to make http requests out of the box.
Currently, Guzzle 6 is the only supported library.
```bash
composer require tebru/retrofit-php-http-guzzle6
```
```php
Retrofit::builder()
->setHttpClient(new Guzzle6HttpClient(new Client()));
```
This implements `Tebru\Retrofit\HttpClient` which has methods to make
requests synchronously and asynchronously using PSR-7 request objects.
Custom Call Adapters
--------------------
The default call adapter doesn't modify the Call at all. Implement a
`Tebru\Retrofit\CallAdapter` and `Tebru\Retrofit\CallAdapterFactory`
if you want to change the return type from service methods.
This supporting library doesn't exist yet, but if you wanted to use
Retrofit with Rx-PHP, you could do so like this.
```php
/**
* @GET("/")
*/
public function test(): Observable;
```
```php
Retrofit::builder()
->addCallAdapterFactory(new RxCallAdapterFactory());
```
```php
interface CallAdapterFactory
{
public function supports(TypeToken $type): bool
{
return $type->isA(Observable::class);
}
public function create(TypeToken $type): CallAdapter
{
return new RxCallAdapter();
}
}
```
The `RxCallAdapter` would then wrap the call in an observable and return
that instead of the normal Call.
Custom Proxy
------------
You may find it necessary to alter the default behavior of sending http
requests. This could be for testing purposes, or perhaps because an
api is not built yet and you need to fetch the data somewhere else.
This can be handled by implementing `Tebru\Retrofit\ProxyFactory` and
`\Tebru\Retrofit\Proxy`.
```php
Retrofit::builder()
->addProxyFactory(new CustomProxyFactory());
```
Your proxy should implement the service interface. If you need access to
the default proxy factory, you can implement
`\Tebru\Retrofit\DefaultProxyFactoryAware` which will tell Retrofit to
set the default proxy to your proxy. This is useful if you only need
to override specific methods and want to delegate the rest to the default.
================================================
FILE: docs/annotations.md
================================================
Annotation Reference
====================
A simple example will be provided to show usage for each annotation,
but may not be a completely functioning snippet. All usages may not
have an example.
- [Body](#body)
- [DELETE](#delete)
- [ErrorBody](#errorbody)
- [Field](#field)
- [FieldMap](#fieldmap)
- [GET](#get)
- [HEAD](#head)
- [Header](#header)
- [HeaderMap](#headermap)
- [Headers](#headers)
- [OPTIONS](#options)
- [Part](#part)
- [PartMap](#partmap)
- [PATCH](#patch)
- [Path](#path)
- [POST](#post)
- [PUT](#put)
- [Query](#query)
- [QueryMap](#querymap)
- [QueryName](#queryname)
- [REQUEST](#request)
- [ResponseBody](#responsebody)
- [Url](#url)
Body
----
Defines a json body for the HTTP request.
This annotation sets the content type of the request to
`application/json`. By default, the parameter must be a PSR-7
`StreamInterface`, but adding a converter will allow additional
types to be provided and converted to json.
```php
/**
* @Body("myBody")
*/
public function example(Mybody $myBody): Call;
```
DELETE
------
Performs a DELETE request to the provided path.
```php
/**
* @DELETE("/foo?q=test")
*/
public function example(): Call;
```
ErrorBody
---------
Defines the type the response body should be converted to on errors.
Errors are defined as any http status code outside of 200-300. By
default, only `StreamInterface` is supported, but a converter
will extend the supported types.
```php
/**
* @ErrorBody("App\MyClass")
*/
public function example(): Call;
```
Field
-----
Adds values to a form encoded body.
This annotation sets the request content type to
`application/x-www-form-urlencoded`. The value of this annotation
is both the name of the field and the name of the method parameter.
This can be overridden using the `var` key in the annotation. The
provided argument will be converted to a string before getting applied
to the request. Passing in an array will apply each value to the field
name. The `encoded` key will specify if the data is already encoded,
and therefore shouldn't be re-encoded. This value defaults to false.
```php
/**
* @Field("field1")
* @Field("field2", encoded=true)
* @Field("field3[]", var="field3")
*/
public function example(int $field1, bool $field2, array $field3): Call;
```
FieldMap
--------
This works much like [@Field](#field), except it must be provided an
iterable map. Each key/value pair will be treated as a `@Field` with
the key acting as the field name. The value may be anything that
`@Field` supports.
```php
/**
* @FieldMap("fieldMap")
*/
public function example(Traversable $fieldMap): Call;
```
GET
---
Performs a GET request to the provided path.
```php
/**
* @GET("/foo?q=test")
*/
public function example(): Call;
```
HEAD
----
Performs a HEAD request to the provided path.
```php
/**
* @HEAD("/foo?q=test")
*/
public function example(): Call;
```
Header
------
Add a single header to a request.
The value of this annotation represents both the name of the header
and the parameter name. Header names are lower-cased before getting
added to the request. Headers follow PSR-7 request format, so multiple
values with the same name may be added to the request. Passing an
array as the header value also allows multiple values to be added
under the same header name. Headers will converted to a string before
being applied to the request.
```php
/**
* @Header("X-Foo", var="header1")
* @Header("X-Foo", var="header2")
* @Header("X-Foo", var="header3")
*/
public function example(string $header1, bool $header2, array $header3): Call;
```
The above example will add multiple different values to the `x-foo`
header.
HeaderMap
---------
Allows a way to add multiple headers to a request at once. The annotation
acts much like [FieldMap](#fieldmap), and accepts the same values as
[Header](#header).
```php
/**
* @HeaderMap("headerMap")
*/
public function example(Traversable $headerMap): Call;
```
Headers
-------
Adds headers statically supplied in the annotation value.
```php
/**
* @Headers({
* "X-Foo: Bar",
* "X-Ping: Pong"
* })
*/
```
OPTIONS
-------
Performs an OPTIONS request to the provided path.
```php
/**
* @OPTIONS("/foo?q=test")
*/
public function example(): Call;
```
Part
----
Adds values to a multipart body.
This annotation set the request content type to `multipart/form-data`.
This annotation can accept any value [@Body](#body) can or a special
`MultipartBody` class, which you can use to set additional properties
like headers or the filename. The annotation value specifies the part
name. If you're using `MultipartBody` the part name will be pulled from
the object and the annotation value will be ignored.
```php
/**
* @Part("part1")
* @Part("part2")
*/
public function example(MyBody $part1, MultipartBody $part2): Call;
```
PartMap
-------
This annotation works like previous map annotations. Keys represent
part names, and values may be anything that a [@Part](#part) can
accept. The same rules apply.
```php
/**
* @PartMap("partMap")
*/
public function example(Traversable $partMap): Call;
```
PATCH
-----
Performs a PATCH request to the provided path.
```php
/**
* @PATCH("/foo?q=test")
*/
public function example(): Call;
```
Path
----
This annotation allows you to create a dynamic url, and map path
parts to method parameters. By default, the annotation value will
be the same as the url placeholder and parameter name, but this is
overridable by using the `var` annotation key. Path values will get
converted to strings before getting added to the url.
```php
/**
* @GET("/foo/{user}/{foo-bar}"
* @Path("user")
* @Path("foo-bar", var="fooBar")
*/
public function example(int $user, string $fooBar): Call;
```
POST
----
Performs a POST request to the provided path.
```php
/**
* @POST("/foo?q=test")
*/
public function example(): Call;
```
PUT
---
Performs a PUT request to the provided path.
```php
/**
* @PUT("/foo?q=test")
*/
public function example(): Call;
```
Query
-----
Add a query to the url.
The annotation value represents the query name. The value will be
converted to a string before being added to the url. Use the `var`
key if the parameter name doesn't match the query name and the `encoded`
key to specify if the query data is already encoded. This defaults to
false. Passing an array will apply each value to to the specified name.
```php
/**
* @Query("query1")
* @Query("query2", encoded=true)
* @Query("query3[]", var="field3")
*/
public function example(int $query1, bool $query2, array $query3): Call;
```
QueryMap
--------
Adds multiple queries to a url.
This annotation works the same as other map annotations and adds
key/value pairs of queries to a url where keys represent the query name
and the value may be anything that [@Query](#query) allows.
```php
/**
* @QueryMap("queryMap")
*/
public function example(Traversable $queryMap): Call;
```
QueryName
---------
Adds only the query name part of a query, excluding the `=` and anything
after it. The annotation value represents the name (which can be changed
with the `var` key), and you can specify that the data is already encoded
with the `encoded` key. Data is converted to a string before getting
added to the url.
```php
/**
* @QueryName("queryName1")
* @QueryName("queryName2", encoded=true)
*/
public function example(float $queryName1, string $queryName2): Call;
```
REQUEST
-------
Performs a custom request method to the provided path.
Additionally, the method type and whether the request contains a body
must be specified.
```php
/**
* @REQUEST("/foo?q=test", type="FOO", body=false)
*/
public function example(): Call;
```
ResponseBody
------------
Defines the type the response body should be converted to on success.
Successful responses are defined as any http status code with 200-300.
By default, only `StreamInterface` is supported, but a converter
will extend the supported types.
```php
/**
* @ResponseBody("App\MyClass")
*/
public function example(): Call;
```
Url
---
This annotation allows changing the base url of a request. This will
allow overriding whatever url is specified on the `RetrofitBuilder`.
```php
/**
* @Url("baseUrl")
*/
public function example(string $baseUrl): Call;
```
================================================
FILE: docs/installation.md
================================================
Installation
============
Installation with Composer
--------------------------
```bash
composer require tebru/retrofit-php:~3.0
```
Retrofit does not include an http client, please install an
implementation.
```bash
composer require tebru/retrofit-php-http-guzzle6
```
Retrofit does not support converting request or response bodies outside
PSR-7 `StreamInterface`. Install a converter to handle custom
conversions.
```bash
composer require tebru/retrofit-php-converter-gson
```
Setup
-----
The easiest way to get setup is to add the psr-4 autoload location where
you include the autoloader.
```php
$loader = require __DIR__ . '/vendor/autoload.php';
$loader->addPsr4('Tebru\\Retrofit\\Proxy\\', __DIR__ . '</path/to/cache/dir>/retrofit');
```
If you do not have environment specific cache directories, you could
specify the psr-4 autoload location in your composer.json instead.
```json
"autoload": {
"psr-4": {
"Tebru\\Retrofit\\Proxy\\": "<path/to/cache/dir>/retrofit"
}
}
```
================================================
FILE: docs/upgrade_2_3.md
================================================
Upgrading from Retrofit 2 to Retrofit 3
=======================================
Retrofit 3 is a complete rewrite of Retrofit 2 in an attempt to make
the library operate more closely with the Square version. This includes
changing how annotations operate. Therefore, creating a friendly upgrade
path would have been extremely time consuming, and not that useful, as
nearly everything has been deprecated.
- [Overview](#overview)
- [Feature Updates](#feature-updates)
- [Dependency Updates](#dependency-updates)
- [File Updates](#file-updates)
- [Annotation Updates](#annotation-updates)
Overview
--------
The way that requests are sent and responses are handled has changed
dramatically. Walking through how the steps have changed is the best
way to illustrate these differences.
### Retrofit 2
1. Collect all services class names
2. Read annotations and generate PHP code based on annotations
3. Cache code on filesystem
4. Use RestAdapterBuilder to build a RestAdapter instance
5. Use RestAdapter to load the service class from the filesystem and return instance
6. Call methods on the service to make http requests
7. Return expected response based on annotations
### Retrofit 3
1. Use RetrofitBuilder to build a Retrofit instance
2. Use Retrofit to ask for a Proxy object based on interface class name
3. Generate proxy object, which is an implementation of the provided interface
4. Call methods on proxy object, which internally reads annotations and
returns a Call object
5. Use the call object to execute a request synchronously or asynchronously,
and returns a Response object
6. Fetch deserialized response body from Response object based on success/failure
of request
The new method has several advantages.
- Nothing is written to the filesystem unless caching is enabled. This
fixes some issues with interfaces changing in development.
- All requests can be made asynchronously. Previously a Callback needed
to be defined in the method signature to enable async requests.
- There is more flexibility in getting different deserialized response
bodies depending on the success or failure of the request.
- The code is testable, as the only generated code is tiny and the same
for every method as it just delegate to a real class. Previously testing
what got generated was painful and prone to error.
In addition, the generated class namespace has changed from
`Tebru\Retrofit\Generated` to `Tebru\Retrofit\Proxy`.
Feature Updates
---------------
The following are high level feature changes
- The event system has been removed
- All methods require a return type (this must be `Call` if not using a custom adapter)
- All method parameter require a typehint
- All types are supported for all annotations mapped to a parameter. This
is handled through the converter system.
- Custom annotations may be used to modify the request
Dependency Updates
------------------
### Upgraded
- php: from 5.4 to 7.1
- nikic/php-parer: from ~1.3 to ~3.0.6
### Added
- symfony/cache (switched from doctrine/cache)
- tebru/doctrine-annotation-reader
- tebru/php-type
### Removed
- jms/serializer
- phpoption/phpption
- tebru/assert
- tebru/dynamo
- tebru/retrofit-http-clients
- symfony/event-dispatcher
- symfony/filesystem
- psr/log
- doctrine/cache
- doctrine/collections
Additionally removed the coveralls binary that was used for pushing
code coverage to Coveralls. This unfortunately added Guzzle 3 classes,
which could be confusing if the binary wasn't ignored.
File Updates
------------
There are many new files, but the directory structure has changed. There
is now a an `Internal` package that represents classes that should only
be used internally by the library. They may change between releases,
but will not represent a BC break. All classes will be referenced through
an interface or abstract class with few exceptions.
### Removed Directories
The following directories and all files have been removed.
- src/Adapter
- src/Generation
- src/Event
- src/Exception
- src/Subscriber
### Removed Files
The following files have been removed.
- src/Http/AsyncAware.php
- src/Http/Callback.php
- src/Http/Response.php
Annotation Updates
------------------
Many annotations have changed behavior. Additionally, annotations have
been added, removed, and renamed.
### Changed Annotations
- @Body now only works with application/json requests
- @Body may not appear with requests that do not have a body
- @Body removed jsonSerializable option, use a custom converter instead
- @GET, @HEAD, @OPTIONS, @DELETE may no longer be used with an request that contains a body
- @Part now specifically denotes a multipart request
- @Part now allows specification of encoding type (defaults to binary)
- @Part must accept a body value that can be converted, or the new MultipartBody
- @PartMap is an iterable where keys are names as strings and values are the same as @Part
- @Query added ability to specify whether data is pre-encoded or not
- Passing an array to a @Query parameter will now create separate
parameters with the name as the query name and the value as one of the array
- @QueryMap is now an iterable with supported values the same as @Query
### Added Annotations
- @Field: Used only for form encoded request bodies. An array will add all values to the field name
- @FieldMap, @HeaderMap: Like corresponding annotation, but for any iterable where string keys are the name
- @Path is new and required to map url placeholders to parameters
- @QueryName allows adding a query parameter that doesn’t have a value
- @REQUEST is a generic http request annotation like @GET or @POST
### Removed Annotations
- @FormUrlEncoded - use @Field/@FieldMap instead
- @JsonBody - use @Body instead
- @Multipart - use @Part/@PartMap instead
- @ResponseType – this was specifically created to handle cases where a response instead of body was returned and is no longer needed
- @SerializationContext/@DeserializationContext - jms/serializer was removed from project
### Renamed Annotations
- @BaseUrl to @Url
- @Returns to @ResponseBody
================================================
FILE: docs/usage.md
================================================
Usage
=====
To start, it would be a good idea to get an understanding of what
Retrofit is doing under the hood.
Let's assume we're using the following service definition
```php
interface GitHubService
{
/**
* @GET("/users/{user}/list")
* @Path("user")
* @Query("limit")
*/
public function listRepos(string $user, int $limit): Call;
}
```
First, you need an instance of `Retrofit`. Here is the easiest way to
create one.
```php
$retrofit = Retrofit::builder()
->setBaseUrl('http://api.example.com')
->setHttpClient(new Guzzle6HttpClient(new Client()))
->build();
```
Creating a `GitHubService` implementation is easy
```php
$githubService = $retrofit->create(GitHubService::class);
```
This is going to generate a `Proxy` object that implements the
`GitHubService` interface. You can use the proxy like you normally would
use an instance of `GitHubService`.
```php
$call = $githubService->listRepos('octocat', 10);
```
This will parse the `GitHubService` interface, and store the following
information about the `listRepos` method:
1. It should make a `GET` request to `/users/{user}/list`
2. `{user}` is provided by the `$user` parameter, which is a string
3. `limit` is a query parameter and provided by `$limit`, which is an integer
3. We're returning a `Call` object
4. We provided two arguments and they're `octocat` and `10`
This information is available in the `Call` object, so when you're ready
to make the request, it has enough information to construct the request.
Doing so can be done synchronously or asynchronously. Here's an example
of a synchronous request
```php
$response = $call->execute();
```
And asynchronously
```php
$call->enqueue(
function(Response $response) {
// handle any response (200, 404, 500)
},
function(Throwable $throwable) {
// handle an exception (network unreachable)
}
);
$call->wait();
```
Both of the callbacks are optional. If you just wanted to make the
request and didn't care about the response, you could do this instead
```php
$call->enqueue();
$call->wait();
```
Calling `->wait()` is necessary to trigger sending the requests. Using
the guzzle client, the requests are sent in batches of 5 using a Pool.
This allows callbacks to trigger as soon as one response is received.
`Response` here is a Retrofit Response (`Tebru\Retrofit\Response`). It
provides an easy way to check if the request was successful (if the
status code is between 200 and 300) and methods for getting the success
or error response bodies.
```php
if ($response->isSuccessful()) {
// success body
$response->body(); // StreamInterface
} else {
// error body
$response->errorBody(); // StreamInterface
}
```
By default, Retrofit only works with a PSR-7 `StreamInterface` for
setting request bodies and handling response bodies. This is somewhat
limiting, but can be enhanced by using converters. The converter I'm
going to demonstrate uses [Gson](https://github.com/tebru/gson-php)
because it can handle the conversion from any type to json and back.
Adding it to the builder is simple
```php
Retrofit::builder()
// ...
->addConverterFactory(new GsonConverterFactory(Gson::builder()->build()))
// ...
```
And allows using a lot more annotations of the client. Here's an example
using the `ResponseBody` and `ErrorBody` annotations to inform Retrofit
how to convert success and error responses
```php
interface GitHubService
{
/**
* @GET("/users/{user}/list")
* @Path("user")
* @Query("limit")
* @ResponseBody("App\GithubService\ListRepo")
* @ErrorBody("App\GitHubService\ApiError")
*/
public function listRepos(string $user, int $limit): Call;
}
```
The annotation value is the class name that the response should be
deserialized into. This allows for cleaner handling of responses
```php
if (!$response->isSuccessful()) {
// throw an exception with the deserialized body to be handled elsewhere
throw new ApiException($response->errorBody());
}
$listRepos = $response->body();
foreach ($listRepos as $repo) {
// iterate over the repo list
}
```
Converters can also be used when sending json. For example, assume this
service definition
```php
/**
* @ErrorBody("App\GitHubService\ApiError")
*/
interface GitHubService
{
/**
* @POST("/user/repos")
* @Body("repo")
* @ResponseBody("App\GitHubService\Repo")
*/
public function createRepo(Repo $repo): Call;
}
```
Because we have already registered a converter that can handle any
object, Retrofit will make a POST request to `/user/repos`, convert the
`Repo` object to json, add a `Content-Type` header of `application/json`,
and deserialize the response into a `App\GitHubService\Repo` object.
We've discussed `RequestBodyConverter`s and `ResponseBodyConverters`s.
The third type is a `StringConverter`. Adding query parameters to the
request was introduced earlier and uses a string converter to convert
various types to strings.
As seen before, we were able to easily convert an integer to a string.
The same works for a string, float, and boolean types. Booleans get
converted to `"true"` or `"false"`.
However, using an array as a query parameter has a unique effect as it
will apply each item in the array to the same query key. Let's look at
an example.
```php
interface GitHubService
{
/**
* @GET("/user/list")
* @Query("affiliation[]", var="affiliations")
*/
public function listRepos(array $affiliations): Call;
}
$githubService->listRepos(['owner', 'collaborator']);
```
*The actual GitHub API passes affiliations as a comma separated string,
but for the purposes of this example, we'll pretend it's in array
syntax.*
Here we're using the `var` key to tell Retrofit to not look for a
parameter called `$affiliation[]`—as that's illegal—but `$affiliations`
instead. This will create the query string
```
affiliation[]=owner&affiliation[]=collaborator
```
The `[]` at the end is not magic, Retrofit would behave the same way if
it was missing. It just tells servers that this data is an array.
Multiple `@Query` annotations may be used on the same method
```php
interface GitHubService
{
/**
* @GET("/user/list")
* @Query("affiliation[]", var="affiliations")
* @Query("sort")
*/
public function listRepos(array $affiliations, string $sort): Call;
}
```
This can tend to get lengthy, which is where `@QueryMap` becomes useful.
A QueryMap is just an iterable hash of string keys and values. The
values can be anything that can be passed to a regular `@Query`. Here's
an example using an array
```php
interface GitHubService
{
/**
* @GET("/user/list")
* @QueryMap("queries")
*/
public function listRepos(array $queries): Call;
}
$githubService->listRepos([
'affiliations[]' => ['owner', 'collaborator'],
'sort' => 'created',
]);
```
But an object that implements `Iterable` could also be used
```php
interface GitHubService
{
/**
* @GET("/user/list")
* @QueryMap("queries")
*/
public function listRepos(ListReposQueries $queries): Call;
}
$githubService->listRepos(new ListReposQueries());
```
Parameters (and all parameters) are optional and accept default values.
Specifying default null here will not send any query parameters.
```php
interface GitHubService
{
/**
* @GET("/user/list")
* @QueryMap("queries")
*/
public function listRepos(?ListReposQueries $queries = null): Call;
}
$githubService->listRepos();
```
This behavior is consistent with `@Field`/`@FieldMap` and
`@Header`/`@HeaderMap`.
Please see the [Annotation Reference](annotations.md) for a more in-depth
look at the different annotations and how they function.
================================================
FILE: phpcs.xml
================================================
<?xml version="1.0"?>
<ruleset>
<file>./src</file>
<rule ref="PSR1" />
</ruleset>
================================================
FILE: phpunit.xml
================================================
<phpunit colors="true" bootstrap="tests/bootstrap.php">
<testsuites>
<testsuite name="Unit">
<directory>tests/Unit</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>src</directory>
</whitelist>
</filter>
<logging>
<log type="coverage-clover" target="build/logs/clover.xml"/>
<log type="coverage-html" target="build/logs/html"/>
</logging>
</phpunit>
================================================
FILE: src/Annotation/Body.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\Retrofit\RequestBodyConverter;
/**
* Define the body of the HTTP request.
*
* This annotation may only be used on requests that are sending json. The parameter
* this annotation maps to must be able to be converted to json.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class Body extends ParameterAnnotation
{
/**
* Return the converter interface class
*
* Can be one of RequestBodyConverter, ResponseBodyConverter, or StringConverter
*
* @return string
*/
public function converterType(): ?string
{
return RequestBodyConverter::class;
}
}
================================================
FILE: src/Annotation/DELETE.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\AnnotationReader\AbstractAnnotation;
/**
* Defines an HTTP DELETE request type to a REST path relative to base URL.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class DELETE extends AbstractAnnotation implements HttpRequest
{
/**
* Returns the type of the annotation (get, post, put, etc)
*
* @return string
*/
public function getType(): string
{
return 'delete';
}
/**
* Returns true if the request type contains a body
*
* @return bool
*/
public function hasBody(): bool
{
return false;
}
}
================================================
FILE: src/Annotation/Encodable.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\Retrofit\StringConverter;
/**
* Abstract class that adds an 'encoded' boolean key to annotations to
* represent data that is already encoded or not.
*
* @author Nate Brunette <n@tebru.net>
*/
abstract class Encodable extends ParameterAnnotation
{
/**
* The values are already encoded
*
* @var bool
*/
private $encoded;
/**
* Initialize annotation data
*/
protected function init(): void
{
parent::init();
$this->encoded = $this->data['encoded'] ?? false;
}
/**
* Returns true if the values are already encoded
*
* @return bool
*/
public function isEncoded(): bool
{
return $this->encoded;
}
/**
* Return the converter interface class
*
* Can be one of RequestBodyConverter, ResponseBodyConverter, or StringConverter
*
* @return string
*/
public function converterType(): ?string
{
return StringConverter::class;
}
}
================================================
FILE: src/Annotation/ErrorBody.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\AnnotationReader\AbstractAnnotation;
/**
* Use this annotation to define a class that errors should be deserialized as
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class ErrorBody extends AbstractAnnotation
{
}
================================================
FILE: src/Annotation/Field.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
/**
* Adds a field to form encoded requests
*
* The default value represents the field name. Passing an array will add a mapping between the
* field name and each value in the array. The array must be 0-indexed.
*
* Set 'encoded' to true to specify that the data is already encoded.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class Field extends Encodable
{
/**
* Whether or not multiple annotations of this type can
* be added to a method
*
* @return bool
*/
public function allowMultiple(): bool
{
return true;
}
}
================================================
FILE: src/Annotation/FieldMap.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
/**
* Represents a collection of @Field annotations
*
* The default value specifies the variable name in the method signature.
*
* Any iterable may be passed as an argument. Keys of the map will be the field
* names, and the values will be handled exactly the same as as @Field.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class FieldMap extends Encodable
{
/**
* Returns true if multiple annotations of this type are allowed
*
* @return bool
*/
public function allowMultiple(): bool
{
return true;
}
}
================================================
FILE: src/Annotation/GET.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\AnnotationReader\AbstractAnnotation;
/**
* Defines an HTTP GET request type to a REST path relative to base URL.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class GET extends AbstractAnnotation implements HttpRequest
{
/**
* Returns the type of the annotation (get, post, put, etc)
*
* @return string
*/
public function getType(): string
{
return 'get';
}
/**
* Returns true if the request type contains a body
*
* @return bool
*/
public function hasBody(): bool
{
return false;
}
}
================================================
FILE: src/Annotation/HEAD.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\AnnotationReader\AbstractAnnotation;
/**
* Defines an HTTP HEAD request type to a REST path relative to base URL.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class HEAD extends AbstractAnnotation implements HttpRequest
{
/**
* Returns the type of the annotation (get, post, put, etc)
*
* @return string
*/
public function getType(): string
{
return 'head';
}
/**
* Returns true if the request type contains a body
*
* @return bool
*/
public function hasBody(): bool
{
return false;
}
}
================================================
FILE: src/Annotation/Header.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\Retrofit\StringConverter;
/**
* Adds a header to request
*
* The default value represents the header name. Passing an array will add a new value for each
* header name.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class Header extends ParameterAnnotation
{
/**
* Whether or not multiple annotations of this type can
* be added to a method
*
* @return bool
*/
public function allowMultiple(): bool
{
return true;
}
/**
* Return the converter interface class
*
* Can be one of RequestBodyConverter, ResponseBodyConverter, or StringConverter
*
* @return string
*/
public function converterType(): ?string
{
return StringConverter::class;
}
}
================================================
FILE: src/Annotation/HeaderMap.php
================================================
<?php
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\Retrofit\StringConverter;
/**
* Represents a collection of @Header annotations
*
* The default value specifies the variable name in the method signature.
*
* Any iterable may be passed as an argument. Keys of the map will be the header
* names, and the values will be handled exactly the same as as @Header.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class HeaderMap extends ParameterAnnotation
{
/**
* Return the converter interface class
*
* Can be one of RequestBodyConverter, ResponseBodyConverter, or StringConverter
*
* @return string
*/
public function converterType(): ?string
{
return StringConverter::class;
}
/**
* Returns true if multiple annotations of this type are allowed
*
* @return bool
*/
public function allowMultiple(): bool
{
return true;
}
}
================================================
FILE: src/Annotation/Headers.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use RuntimeException;
use Tebru;
use Tebru\AnnotationReader\AbstractAnnotation;
/**
* Adds headers statically supplied in the value.
*
* @Headers("Cache-Control: max-age=640000")
* @Headers({
* "X-Foo: Bar",
* "X-Ping: Pong"
* })
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class Headers extends AbstractAnnotation
{
/**
* Initialize annotation data
*
* @throws \RuntimeException
*/
protected function init(): void
{
// loop through each string and break on ':'
foreach ((array)$this->getValue() as $header) {
$position = strpos($header, ':');
if ($position === false) {
throw new RuntimeException('Retrofit: Header in an incorrect format. Expected "Name: value"');
}
$name = trim(substr($header, 0, $position));
$value = trim(substr($header, $position + 1));
$this->value[$name] = $value;
}
}
}
================================================
FILE: src/Annotation/HttpRequest.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru;
/**
* Interface for the different http request annotations (e.g. @GET, @POST)
*
* @author Nate Brunette <n@tebru.net>
*/
interface HttpRequest
{
/**
* Returns the type of the annotation (get, post, put, etc)
*
* @return string
*/
public function getType(): string;
/**
* Returns true if the request type contains a body
*
* @return bool
*/
public function hasBody(): bool;
/**
* Get the url value
*
* @return mixed
*/
public function getValue();
}
================================================
FILE: src/Annotation/OPTIONS.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\AnnotationReader\AbstractAnnotation;
/**
* Defines an HTTP OPTIONS request type to a REST path relative to base URL.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class OPTIONS extends AbstractAnnotation implements HttpRequest
{
/**
* Returns the type of the annotation (get, post, put, etc)
*
* @return string
*/
public function getType(): string
{
return 'options';
}
/**
* Returns true if the request type contains a body
*
* @return bool
*/
public function hasBody(): bool
{
return false;
}
}
================================================
FILE: src/Annotation/PATCH.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\AnnotationReader\AbstractAnnotation;
/**
* Defines an HTTP PATCH request type to a REST path relative to base URL.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class PATCH extends AbstractAnnotation implements HttpRequest
{
/**
* Returns the type of the annotation (get, post, put, etc)
*
* @return string
*/
public function getType(): string
{
return 'patch';
}
/**
* Returns true if the request type contains a body
*
* @return bool
*/
public function hasBody(): bool
{
return true;
}
}
================================================
FILE: src/Annotation/POST.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\AnnotationReader\AbstractAnnotation;
/**
* Defines an HTTP POST request type to a REST path relative to base URL.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class POST extends AbstractAnnotation implements HttpRequest
{
/**
* Returns the type of the annotation (get, post, put, etc)
*
* @return string
*/
public function getType(): string
{
return 'post';
}
/**
* Returns true if the request type contains a body
*
* @return bool
*/
public function hasBody(): bool
{
return true;
}
}
================================================
FILE: src/Annotation/PUT.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\AnnotationReader\AbstractAnnotation;
/**
* Defines an HTTP PUT request type to a REST path relative to base URL.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class PUT extends AbstractAnnotation implements HttpRequest
{
/**
* Returns the type of the annotation (get, post, put, etc)
*
* @return string
*/
public function getType(): string
{
return 'put';
}
/**
* Returns true if the request type contains a body
*
* @return bool
*/
public function hasBody(): bool
{
return true;
}
}
================================================
FILE: src/Annotation/ParameterAnnotation.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru;
use Tebru\AnnotationReader\AbstractAnnotation;
/**
* Parent class for annotation that have a [parameterName => $parameterName] format
*
* @author Nate Brunette <n@tebru.net>
*/
abstract class ParameterAnnotation extends AbstractAnnotation implements ParameterAwareAnnotation
{
/**
* An alias for the variable name
*
* @var string $var
*/
private $var;
/**
* Initialize annotation data
*/
protected function init(): void
{
parent::init();
$this->var = $this->data['var'] ?? null;
}
/**
* Get the variable name
*
* @return string
*/
public function getVariableName(): string
{
return $this->var ?? $this->getValue();
}
}
================================================
FILE: src/Annotation/ParameterAwareAnnotation.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
/**
* Interface ParameterAwareAnnotation
*
* Annotations that implement this interface are mapped to method parameters, so they
* need to know a variable name to reference and how to convert the value.
*
* @author Nate Brunette <n@tebru.net>
*/
interface ParameterAwareAnnotation
{
/**
* The variable name, which will either be the default value or the value of 'var' if
* specified. The variable name excludes the '$'.
*
* @return string
*/
public function getVariableName(): string;
/**
* Return the converter interface class
*
* Can be one of RequestBodyConverter, ResponseBodyConverter, or StringConverter
*
* @return null|string
*/
public function converterType(): ?string;
}
================================================
FILE: src/Annotation/Part.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\Retrofit\Http\MultipartBody;
use Tebru\Retrofit\RequestBodyConverter;
/**
* Denotes a single part of a multipart request.
*
* The default value represents the part name. Passing a [@see MultipartBody] will use the values from
* that object, otherwise the value will be converted to a stream and added to the request.
*
* Use the 'encoding' key to override the default 'binary' encoding.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class Part extends ParameterAnnotation
{
/**
* How the multipart request is encoded
*
* @var string
*/
private $encoding;
/**
* Initialize annotation data
*/
protected function init(): void
{
parent::init();
$this->encoding = $this->data['encoding'] ?? 'binary';
}
/**
* Get the encoding type
*
* @return string
*/
public function getEncoding(): string
{
return $this->encoding;
}
/**
* Whether or not multiple annotations of this type can
* be added to a method
*
* @return bool
*/
public function allowMultiple(): bool
{
return true;
}
/**
* Return the converter interface class
*
* Can be one of RequestBodyConverter, ResponseBodyConverter, or StringConverter
*
* @return string
*/
public function converterType(): ?string
{
return RequestBodyConverter::class;
}
}
================================================
FILE: src/Annotation/PartMap.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\Retrofit\Http\MultipartBody;
use Tebru\Retrofit\RequestBodyConverter;
/**
* Represents a collection of @Part annotations
*
* The default value specifies the variable name in the method signature.
*
* Any iterable may be passed as an argument.
*
* If the item is not a [@see MultipartBody], keys of the map will be the field
* names, and the values will be handled exactly the same as as @Part. Otherwise,
* all values of the MultipartBody will be used.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class PartMap extends ParameterAnnotation
{
/**
* How the multipart request is encoded
*
* @var string
*/
private $encoding;
/**
* Initialize annotation data
*/
protected function init(): void
{
parent::init();
$this->encoding = $this->data['encoding'] ?? 'binary';
}
/**
* Get the encoding type
*
* @return string
*/
public function getEncoding(): string
{
return $this->encoding;
}
/**
* Return the converter interface class
*
* Can be one of RequestBodyConverter, ResponseBodyConverter, or StringConverter
*
* @return string
*/
public function converterType(): ?string
{
return RequestBodyConverter::class;
}
/**
* Returns true if multiple annotations of this type are allowed
*
* @return bool
*/
public function allowMultiple(): bool
{
return true;
}
}
================================================
FILE: src/Annotation/Path.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\Retrofit\StringConverter;
/**
* Specifies replaceable parts in a url path
*
* Given this service method
*
* /**
* * @GET("/foo/{my-path}")
* * @Path("my-path", var="myPath")
* *
* public function foo(string $myPath): Call;
*
* Passing in "bar" will result in the path "/foo/bar". The 'var' key is unnecessary if
* the path value inside the {} matches the variable name.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class Path extends ParameterAnnotation
{
/**
* Returns true if multiple annotations of this type are allowed
*
* @return bool
*/
public function allowMultiple(): bool
{
return true;
}
/**
* Return the converter interface class
*
* Can be one of RequestBodyConverter, ResponseBodyConverter, or StringConverter
*
* @return string
*/
public function converterType(): ?string
{
return StringConverter::class;
}
}
================================================
FILE: src/Annotation/Query.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
/**
* Adds a query to the url path
*
* The default value represents the query name. Passing an array will add a mapping between the
* query name and each value in the array. The array must be 0-indexed.
*
* Set 'encoded' to true to specify that the data is already encoded.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class Query extends Encodable
{
/**
* Whether or not multiple annotations of this type can
* be added to a method
*
* @return bool
*/
public function allowMultiple(): bool
{
return true;
}
}
================================================
FILE: src/Annotation/QueryMap.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
/**
* Represents a collection of @Query annotations
*
* The default value specifies the variable name in the method signature.
*
* Any iterable may be passed as an argument. Keys of the map will be the query
* names, and the values will be handled exactly the same as as @Query.
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class QueryMap extends Encodable
{
/**
* Returns true if multiple annotations of this type are allowed
*
* @return bool
*/
public function allowMultiple(): bool
{
return true;
}
}
================================================
FILE: src/Annotation/QueryName.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
/**
* Query parameter appended to the URL
*
* Use this annotation if the query parameter does not have a value
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class QueryName extends Encodable
{
/**
* Whether or not multiple annotations of this type can
* be added to a method
*
* @return bool
*/
public function allowMultiple(): bool
{
return true;
}
}
================================================
FILE: src/Annotation/REQUEST.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\AnnotationReader\AbstractAnnotation;
/**
* Defines an HTTP request type to a REST path relative to base URL.
*
* This is a generic annotation that lets you define an http request
* outside the standard GET, POST, etc
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class REQUEST extends AbstractAnnotation implements HttpRequest
{
/**
* The request method
*
* @var string
*/
private $type;
/**
* If the request contains a body
*
* @var bool
*/
private $body;
/**
* Initialize annotation data
*/
protected function init(): void
{
$this->assertKey('type');
$this->type = $this->data['type'];
$this->body = $this->data['body'] ?? false;
}
/**
* Returns the type of the annotation (get, post, put, etc)
*
* @return string
*/
public function getType(): string
{
return $this->type;
}
/**
* Returns true if the request type contains a body
*
* @return bool
*/
public function hasBody(): bool
{
return $this->body;
}
}
================================================
FILE: src/Annotation/ResponseBody.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\AnnotationReader\AbstractAnnotation;
/**
* Use this annotation to define a class that responses should be deserialized as
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class ResponseBody extends AbstractAnnotation
{
}
================================================
FILE: src/Annotation/Url.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Annotation;
use Tebru\Retrofit\StringConverter;
/**
* Use this annotation to override the base url
*
* @author Nate Brunette <n@tebru.net>
*
* @Annotation
* @Target({"CLASS", "METHOD"})
*/
class Url extends ParameterAnnotation
{
/**
* Return the converter interface class
*
* Can be one of RequestBodyConverter, ResponseBodyConverter, or StringConverter
*
* @return null|string
*/
public function converterType(): ?string
{
return StringConverter::class;
}
}
================================================
FILE: src/AnnotationHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit;
use Tebru\AnnotationReader\AbstractAnnotation;
/**
* Interface AnnotationHandler
*
* Implementations of this interface accept an annotation and manipulate the [@see ServiceMethodBuilder] based
* on the value.
*
* @author Nate Brunette <n@tebru.net>
*/
interface AnnotationHandler
{
/**
* Handle an annotation, mutating the [@see ServiceMethodBuilder] based on the value
*
* @param AbstractAnnotation $annotation The annotation to handle
* @param ServiceMethodBuilder $serviceMethodBuilder Used to construct a [@see ServiceMethod]
* @param Converter|StringConverter|RequestBodyConverter|null $converter Converter used to convert types before sending to service method
* @param int|null $index The position of the parameter or null if annotation does not reference parameter
* @return void
*/
public function handle(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
?Converter $converter,
?int $index
): void;
}
================================================
FILE: src/Call.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit;
use Psr\Http\Message\RequestInterface;
/**
* Interface Call
*
* Implementations will be able to make requests synchronously or asynchronously and will
* be able to provide a PSR-7 request object.
*
* @author Nate Brunette <n@tebru.net>
*/
interface Call
{
/**
* Execute request synchronously
*
* A [@see Response] will be returned
*
* @return Response
*/
public function execute(): Response;
/**
* Execute request asynchronously
*
* This method accepts two optional callbacks.
*
* onResponse() will be called for any request that gets a response,
* whether it was successful or not. It will send a [@see Call] and
* a [@see Response] as the first and second parameters.
*
* onFailure() will be called in the event a network request failed. It
* will send the [@see Throwable] that was encountered.
*
* Example of method signatures:
*
* $call->enqueue(
* function (\Tebru\Retrofit\Call $call, \Tebru\Retrofit\Response $response) {},
* function (\Throwable $throwable) {}
* );
*
* @param callable $onResponse On any response
* @param callable $onFailure On any network request failure
* @return Call
*/
public function enqueue(?callable $onResponse = null, ?callable $onFailure = null): Call;
/**
* When making requests asynchronously, call wait() to execute the requests
*
* @return void
*/
public function wait(): void;
/**
* Get the PSR-7 request
*
* @return RequestInterface
*/
public function request(): RequestInterface;
}
================================================
FILE: src/CallAdapter.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit;
/**
* Interface CallAdapter
*
* Implementors can modify a [@see Call] to match the expected service method
* return type.
*
* @author Nate Brunette <n@tebru.net>
*/
interface CallAdapter
{
/**
* Accepts a [@see Call] and converts it to the appropriate type
*
* @param Call $call
* @return mixed
*/
public function adapt(Call $call);
}
================================================
FILE: src/CallAdapterFactory.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit;
use Tebru\PhpType\TypeToken;
/**
* Interface CallAdapterFactory
*
* @author Nate Brunette <n@tebru.net>
*/
interface CallAdapterFactory
{
/**
* Returns true if the factory supports this type
*
* @param TypeToken $type
* @return bool
*/
public function supports(TypeToken $type): bool;
/**
* Create a new factory from type
*
* @param TypeToken $type
* @return CallAdapter
*/
public function create(TypeToken $type): CallAdapter;
}
================================================
FILE: src/Command/CompileCommand.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Command;
use GuzzleHttp\Psr7\Response;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Tebru\Retrofit\HttpClient;
use Tebru\Retrofit\Retrofit;
/**
* Class CompileCommand
*
* @author Nate Brunette <n@tebru.net>
* @codeCoverageIgnore
*/
class CompileCommand extends Command
{
/**
* Configure command
*
* @throws InvalidArgumentException
*/
protected function configure(): void
{
$this->setName('compile');
$this->setDescription('Compiles and caches all services found in the project');
$this->addArgument('sourceDirectory', InputArgument::REQUIRED, 'Enter the source directory');
$this->addArgument('cacheDirectory', InputArgument::REQUIRED, 'Enter the cache directory');
}
/**
* Execute command
*
* @param InputInterface $input
* @param OutputInterface $output
* @return int|null|void
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$srcDir = $input->getArgument('sourceDirectory');
$cacheDir = $input->getArgument('cacheDirectory');
$clientStub = new class implements HttpClient {
/**
* Send a request synchronously and return a PSR-7 [@see ResponseInterface]
*
* @param RequestInterface $request
* @return ResponseInterface
*/
public function send(RequestInterface $request): ResponseInterface
{
return new Response();
}
/**
* Send a request asynchronously
*
* The response callback must be called if any response is returned from the request, and the failure
* callback should only be executed if a request was not completed.
*
* The response callback should pass a PSR-7 [@see ResponseInterface] as the one and only argument. The
* failure callback should pass a [@see Throwable] as the one and only argument.
*
* @param RequestInterface $request
* @param callable $onResponse
* @param callable $onFailure
* @return void
*/
public function sendAsync(RequestInterface $request, callable $onResponse, callable $onFailure): void
{
}
/**
* Calling this method should execute any enqueued requests asynchronously
*
* @return void
*/
public function wait(): void
{
}
};
$retrofit = Retrofit::builder()
->setBaseUrl('')
->setHttpClient($clientStub)
->setCacheDir($cacheDir)
->enableCache()
->build();
$count = $retrofit->createAll($srcDir);
$output->writeln(sprintf('<info>Compiled %s %s successfully</info>', $count, ($count === 1) ? 'class' : 'classes'));
}
}
================================================
FILE: src/Converter.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit;
/**
* Interface Converter
*
* This base interface exists for very basic type awareness
*
* @author Nate Brunette <n@tebru.net>
*/
interface Converter
{
}
================================================
FILE: src/ConverterFactory.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit;
use Tebru\PhpType\TypeToken;
/**
* Interface ConverterFactory
*
* Implementors should return implementations of converters for the types
* that are supported, and null if the type is not supported.
*
* For example, if a converter does not convert types to strings, just return
* null from the stringConverter() method.
*
* @author Nate Brunette <n@tebru.net>
*/
interface ConverterFactory
{
/**
* Return a [@see ResponseBodyConverter] or null
*
* @param TypeToken $type
* @return null|ResponseBodyConverter
*/
public function responseBodyConverter(TypeToken $type): ?ResponseBodyConverter;
/**
* Return a [@see RequestBodyConverter] or null
*
* @param TypeToken $type
* @return null|RequestBodyConverter
*/
public function requestBodyConverter(TypeToken $type): ?RequestBodyConverter;
/**
* Return a [@see StringConverter] or null
*
* @param TypeToken $type
* @return null|StringConverter
*/
public function stringConverter(TypeToken $type): ?StringConverter;
}
================================================
FILE: src/DefaultProxyFactoryAware.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit;
/**
* Any custom proxy factory that implements this interface will get an instance of the default
* proxy factory through a setter. This is useful if you need to temporarily override how requests
* are created for a subset of service methods, but for all other methods, you can delegate to
* the default behavior.
*
* @author Nate Brunette <n@tebru.net>
*/
interface DefaultProxyFactoryAware
{
/**
* Set the default proxy factory
*
* @param ProxyFactory $proxyFactory
* @return void
*/
public function setDefaultProxyFactory(ProxyFactory $proxyFactory): void;
}
================================================
FILE: src/Exception/ResponseHandlingFailedException.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Exception;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use RuntimeException;
use Throwable;
/**
* Class ResponseConversionFailedException
*
* This exception is thrown if there's an issue handling the response. It exists
* in order to provide more information about the request/response to the consumer in
* the event of a failure. It signifies that an HTTP request was successful, but could
* not be properly handled.
*
* @author Nate Brunette <n@tebru.net>
*/
class ResponseHandlingFailedException extends RuntimeException
{
/**
* @var RequestInterface
*/
private $request;
/**
* @var ResponseInterface
*/
private $response;
/**
* Constructor
*
* @param RequestInterface $request
* @param ResponseInterface $response
* @param string $message
* @param Throwable|null $previous
*/
public function __construct(
RequestInterface $request,
ResponseInterface $response,
string $message = '',
Throwable $previous = null
) {
parent::__construct($message, 0, $previous);
$this->request = $request;
$this->response = $response;
}
/**
* @return RequestInterface
*/
public function getRequest(): RequestInterface
{
return $this->request;
}
/**
* @return ResponseInterface
*/
public function getResponse(): ResponseInterface
{
return $this->response;
}
}
================================================
FILE: src/Finder/ServiceResolver.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Finder;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RecursiveRegexIterator;
use RegexIterator;
/**
* Class ServiceResolver
*
* @author Nate Brunette <n@tebru.net>
*/
class ServiceResolver
{
private const ANNOTATION_REGEX = '/Tebru\\\\Retrofit\\\\Annotation/';
private const FILE_REGEX = '/^.+\.php$/i';
private const INTERFACE_REGEX = '/^interface\s+([\w\\\\]+)[\s{\n]?/m';
private const NAMESPACE_REGEX = '/^namespace\s+([\w\\\\]+)/m';
/**
* Find all services given a source directory
*
* @param string $srcDir
* @return string[]
*/
public function findServices(string $srcDir): array
{
$directory = new RecursiveDirectoryIterator($srcDir);
$iterator = new RecursiveIteratorIterator($directory);
$files = new RegexIterator($iterator, self::FILE_REGEX, RecursiveRegexIterator::GET_MATCH);
$services = [];
foreach ($files as $file) {
$fileString = file_get_contents($file[0]);
$annotationMatchesFound = preg_match(self::ANNOTATION_REGEX, $fileString);
if (!$annotationMatchesFound) {
continue;
}
$interfaceMatchesFound = preg_match(self::INTERFACE_REGEX, $fileString, $interfaceMatches);
if (!$interfaceMatchesFound) {
continue;
}
$namespaceMatchesFound = preg_match(self::NAMESPACE_REGEX, $fileString, $namespaceMatches);
$className = '';
if ($namespaceMatchesFound) {
$className .= $namespaceMatches[1];
}
$className .= '\\' . $interfaceMatches[1];
$services[] = $className;
}
return $services;
}
}
================================================
FILE: src/Http/MultipartBody.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Http;
use Psr\Http\Message\StreamInterface;
use function GuzzleHttp\Psr7\stream_for;
/**
* Class MultipartBody
*
* @author Nate Brunette <n@tebru.net>
*/
final class MultipartBody
{
/**
* @var string
*/
private $name;
/**
* @var StreamInterface
*/
private $contents;
/**
* @var array
*/
private $headers;
/**
* @var string
*/
private $filename;
/**
* Constructor
*
* @param string $name
* @param mixed $contents
* @param array[] $headers
* @param null|string $filename
*/
public function __construct(string $name, $contents, array $headers = [], ?string $filename = null)
{
$this->name = $name;
$this->contents = stream_for($contents);
$this->headers = $headers;
$this->filename = $filename;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @return StreamInterface
*/
public function getContents(): StreamInterface
{
return $this->contents;
}
/**
* @return array
*/
public function getHeaders(): array
{
return $this->headers;
}
/**
* @return string|null
*/
public function getFilename(): ?string
{
return $this->filename;
}
}
================================================
FILE: src/HttpClient.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* Interface HttpClient
*
* Implementors will make http requests using PSR-7 request and response objects
*
* @author Nate Brunette <n@tebru.net>
*/
interface HttpClient
{
/**
* Send a request synchronously and return a PSR-7 [@see ResponseInterface]
*
* @param RequestInterface $request
* @return ResponseInterface
*/
public function send(RequestInterface $request): ResponseInterface;
/**
* Send a request asynchronously
*
* The response callback must be called if any response is returned from the request, and the failure
* callback should only be executed if a request was not completed.
*
* The response callback should pass a PSR-7 [@see ResponseInterface] as the one and only argument. The
* failure callback should pass a [@see Throwable] as the one and only argument.
*
* @param RequestInterface $request
* @param callable $onResponse
* @param callable $onFailure
* @return void
*/
public function sendAsync(RequestInterface $request, callable $onResponse, callable $onFailure): void;
/**
* Calling this method should execute any enqueued requests asynchronously
*
* @return void
*/
public function wait(): void;
}
================================================
FILE: src/Internal/AnnotationHandler/BodyAnnotHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\AnnotationHandler;
use InvalidArgumentException;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\Retrofit\AnnotationHandler;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\Internal\ParameterHandler\BodyParamHandler;
use Tebru\Retrofit\RequestBodyConverter;
use Tebru\Retrofit\ServiceMethodBuilder;
/**
* Class BodyAnnotHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class BodyAnnotHandler implements AnnotationHandler
{
/**
* Sets the request method as 'json' and adds a parameter handler for body json data
*
* @param AbstractAnnotation $annotation The annotation to handle
* @param ServiceMethodBuilder $serviceMethodBuilder Used to construct a [@see ServiceMethod]
* @param Converter|RequestBodyConverter $converter Converter used to convert types before sending to service method
* @param int|null $index The position of the parameter or null if annotation does not reference parameter
* @return void
* @throws \InvalidArgumentException
*/
public function handle(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
?Converter $converter,
?int $index
): void {
if (!$converter instanceof RequestBodyConverter) {
throw new InvalidArgumentException(sprintf(
'Retrofit: Converter must be a RequestBodyConverter, %s found',
\gettype($converter)
));
}
$serviceMethodBuilder->setIsJson();
$serviceMethodBuilder->addParameterHandler($index, new BodyParamHandler($converter));
}
}
================================================
FILE: src/Internal/AnnotationHandler/FieldAnnotHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\AnnotationHandler;
use InvalidArgumentException;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\Retrofit\Annotation\Encodable;
use Tebru\Retrofit\Annotation\Field;
use Tebru\Retrofit\AnnotationHandler;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\Internal\ParameterHandler\FieldParamHandler;
use Tebru\Retrofit\ServiceMethodBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class FieldAnnotHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class FieldAnnotHandler implements AnnotationHandler
{
/**
* Set the content type to form encoded and adds a parameter handler for individual fields
*
* @param Field|AbstractAnnotation $annotation The annotation to handle
* @param ServiceMethodBuilder $serviceMethodBuilder Used to construct a [@see ServiceMethod]
* @param Converter|StringConverter $converter Converter used to convert types before sending to service method
* @param int|null $index The position of the parameter or null if annotation does not reference parameter
* @return void
* @throws \InvalidArgumentException
*/
public function handle(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
?Converter $converter,
?int $index
): void {
if (!$annotation instanceof Encodable) {
throw new InvalidArgumentException('Retrofit: Annotation must be encodable');
}
if (!$converter instanceof StringConverter) {
throw new InvalidArgumentException(sprintf(
'Retrofit: Converter must be a StringConverter, %s found',
\gettype($converter)
));
}
$serviceMethodBuilder->setIsFormUrlEncoded();
$serviceMethodBuilder->addParameterHandler(
$index,
new FieldParamHandler($converter, $annotation->getValue(), $annotation->isEncoded())
);
}
}
================================================
FILE: src/Internal/AnnotationHandler/FieldMapAnnotHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\AnnotationHandler;
use InvalidArgumentException;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\Retrofit\Annotation\Encodable;
use Tebru\Retrofit\Annotation\FieldMap;
use Tebru\Retrofit\AnnotationHandler;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\Internal\ParameterHandler\FieldMapParamHandler;
use Tebru\Retrofit\ServiceMethodBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class FieldMapAnnotHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class FieldMapAnnotHandler implements AnnotationHandler
{
/**
* Set the content type to form encoded and adds a parameter handler for a field map
*
* @param FieldMap|AbstractAnnotation $annotation The annotation to handle
* @param ServiceMethodBuilder $serviceMethodBuilder Used to construct a [@see ServiceMethod]
* @param Converter|StringConverter $converter Converter used to convert types before sending to service method
* @param int|null $index The position of the parameter or null if annotation does not reference parameter
* @return void
* @throws \InvalidArgumentException
*/
public function handle(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
?Converter $converter,
?int $index
): void {
if (!$annotation instanceof Encodable) {
throw new InvalidArgumentException('Retrofit: Annotation must be encodable');
}
if (!$converter instanceof StringConverter) {
throw new InvalidArgumentException(sprintf(
'Retrofit: Converter must be a StringConverter, %s found',
\gettype($converter)
));
}
$serviceMethodBuilder->setIsFormUrlEncoded();
$serviceMethodBuilder->addParameterHandler(
$index,
new FieldMapParamHandler($converter, $annotation->isEncoded())
);
}
}
================================================
FILE: src/Internal/AnnotationHandler/HeaderAnnotHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\AnnotationHandler;
use InvalidArgumentException;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\Retrofit\AnnotationHandler;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\Internal\ParameterHandler\HeaderParamHandler;
use Tebru\Retrofit\ServiceMethodBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class HeaderAnnotHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class HeaderAnnotHandler implements AnnotationHandler
{
/**
* Adds header param handler
*
* @param AbstractAnnotation $annotation The annotation to handle
* @param ServiceMethodBuilder $serviceMethodBuilder Used to construct a [@see ServiceMethod]
* @param Converter|StringConverter $converter Converter used to convert types before sending to service method
* @param int|null $index The position of the parameter or null if annotation does not reference parameter
* @return void
* @throws \InvalidArgumentException
*/
public function handle(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
?Converter $converter,
?int $index
): void {
if (!$converter instanceof StringConverter) {
throw new InvalidArgumentException(sprintf(
'Retrofit: Converter must be a StringConverter, %s found',
\gettype($converter)
));
}
$serviceMethodBuilder->addParameterHandler(
$index,
new HeaderParamHandler($converter, $annotation->getValue())
);
}
}
================================================
FILE: src/Internal/AnnotationHandler/HeaderMapAnnotHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\AnnotationHandler;
use InvalidArgumentException;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\Retrofit\AnnotationHandler;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\Internal\ParameterHandler\HeaderMapParamHandler;
use Tebru\Retrofit\ServiceMethodBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class HeaderMapAnnotHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class HeaderMapAnnotHandler implements AnnotationHandler
{
/**
* Adds header map param handler
*
* @param AbstractAnnotation $annotation The annotation to handle
* @param ServiceMethodBuilder $serviceMethodBuilder Used to construct a [@see ServiceMethod]
* @param null|Converter|StringConverter $converter Converter used to convert types before sending to service method
* @param int|null $index The position of the parameter or null if annotation does not reference parameter
* @return void
* @throws \InvalidArgumentException
*/
public function handle(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
?Converter $converter,
?int $index
): void {
if (!$converter instanceof StringConverter) {
throw new InvalidArgumentException(sprintf(
'Retrofit: Converter must be a StringConverter, %s found',
\gettype($converter)
));
}
$serviceMethodBuilder->addParameterHandler($index, new HeaderMapParamHandler($converter));
}
}
================================================
FILE: src/Internal/AnnotationHandler/HeadersAnnotHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\AnnotationHandler;
use InvalidArgumentException;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\Retrofit\AnnotationHandler;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\ServiceMethodBuilder;
/**
* Class HeadersAnnotHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class HeadersAnnotHandler implements AnnotationHandler
{
/**
* Set each header to request
*
* @param AbstractAnnotation $annotation The annotation to handle
* @param ServiceMethodBuilder $serviceMethodBuilder Used to construct a [@see ServiceMethod]
* @param Converter|null $converter Converter used to convert types before sending to service method
* @param int|null $index The position of the parameter or null if annotation does not reference parameter
* @return void
* @throws \InvalidArgumentException
*/
public function handle(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
?Converter $converter,
?int $index
): void {
if ($converter !== null) {
throw new InvalidArgumentException(sprintf(
'Retrofit: Converter must be null, %s found',
\gettype($converter)
));
}
/** @var string[] $headerList */
$headerList = $annotation->getValue();
foreach ($headerList as $name => $header) {
$serviceMethodBuilder->addHeader($name, $header);
}
}
}
================================================
FILE: src/Internal/AnnotationHandler/HttpRequestAnnotHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\AnnotationHandler;
use InvalidArgumentException;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\Retrofit\Annotation\HttpRequest;
use Tebru\Retrofit\AnnotationHandler;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\ServiceMethodBuilder;
/**
* Class HttpRequestAnnotHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class HttpRequestAnnotHandler implements AnnotationHandler
{
/**
* Sets the request method, uri, and whether or not the request contains a body
*
* @param HttpRequest|AbstractAnnotation $annotation The annotation to handle
* @param ServiceMethodBuilder $serviceMethodBuilder Used to construct a [@see ServiceMethod]
* @param Converter|null $converter Converter used to convert types before sending to service method
* @param int|null $index The position of the parameter or null if annotation does not reference parameter
* @return void
* @throws \LogicException
* @throws \InvalidArgumentException
*/
public function handle(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
?Converter $converter,
?int $index
): void {
if (!$annotation instanceof HttpRequest) {
throw new InvalidArgumentException('Retrofit: Annotation must be an HttpRequest');
}
if ($converter !== null) {
throw new InvalidArgumentException(sprintf(
'Retrofit: Converter must be null, %s found',
\gettype($converter)
));
}
$uri = $annotation->getValue();
$serviceMethodBuilder->setMethod($annotation->getType());
$serviceMethodBuilder->setPath($uri);
if (!$annotation->hasBody()) {
$serviceMethodBuilder->setHasBody($annotation->hasBody());
}
}
}
================================================
FILE: src/Internal/AnnotationHandler/PartAnnotHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\AnnotationHandler;
use InvalidArgumentException;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\Retrofit\Annotation\Part;
use Tebru\Retrofit\AnnotationHandler;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\Internal\ParameterHandler\PartParamHandler;
use Tebru\Retrofit\RequestBodyConverter;
use Tebru\Retrofit\ServiceMethodBuilder;
/**
* Class PartAnnotHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class PartAnnotHandler implements AnnotationHandler
{
/**
* Handle an annotation, mutating the [@see ServiceMethodBuilder] based on the value
*
* @param Part|AbstractAnnotation $annotation The annotation to handle
* @param ServiceMethodBuilder $serviceMethodBuilder Used to construct a [@see ServiceMethod]
* @param Converter|RequestBodyConverter $converter Converter used to convert types before sending to service method
* @param int|null $index The position of the parameter or null if annotation does not reference parameter
* @return void
* @throws \InvalidArgumentException
*/
public function handle(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
?Converter $converter,
?int $index
): void {
if (!$annotation instanceof Part) {
throw new InvalidArgumentException('Retrofit: Annotation must be a Part');
}
if (!$converter instanceof RequestBodyConverter) {
throw new InvalidArgumentException(sprintf(
'Retrofit: Converter must be a RequestBodyConverter, %s found',
\gettype($converter)
));
}
$serviceMethodBuilder->setIsMultipart();
$serviceMethodBuilder->addParameterHandler(
$index,
new PartParamHandler($converter, $annotation->getValue(), $annotation->getEncoding())
);
}
}
================================================
FILE: src/Internal/AnnotationHandler/PartMapAnnotHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\AnnotationHandler;
use InvalidArgumentException;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\Retrofit\Annotation\PartMap;
use Tebru\Retrofit\AnnotationHandler;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\Internal\ParameterHandler\PartMapParamHandler;
use Tebru\Retrofit\RequestBodyConverter;
use Tebru\Retrofit\ServiceMethodBuilder;
/**
* Class PartMapAnnotHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class PartMapAnnotHandler implements AnnotationHandler
{
/**
* Add part map param handler
*
* @param PartMap|AbstractAnnotation $annotation The annotation to handle
* @param ServiceMethodBuilder $serviceMethodBuilder Used to construct a [@see ServiceMethod]
* @param Converter|RequestBodyConverter $converter Converter used to convert types before sending to service method
* @param int|null $index The position of the parameter or null if annotation does not reference parameter
* @return void
* @throws \InvalidArgumentException
*/
public function handle(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
?Converter $converter,
?int $index
): void {
if (!$annotation instanceof PartMap) {
throw new InvalidArgumentException('Retrofit: Annotation must be a PartMap');
}
if (!$converter instanceof RequestBodyConverter) {
throw new InvalidArgumentException(sprintf(
'Retrofit: Converter must be a RequestBodyConverter, %s found',
\gettype($converter)
));
}
$serviceMethodBuilder->setIsMultipart();
$serviceMethodBuilder->addParameterHandler(
$index,
new PartMapParamHandler($converter, $annotation->getEncoding())
);
}
}
================================================
FILE: src/Internal/AnnotationHandler/PathAnnotHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\AnnotationHandler;
use InvalidArgumentException;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\Retrofit\AnnotationHandler;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\Internal\ParameterHandler\PathParamHandler;
use Tebru\Retrofit\ServiceMethodBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class PathAnnotHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class PathAnnotHandler implements AnnotationHandler
{
/**
* Add a path param handler
*
* @param AbstractAnnotation $annotation The annotation to handle
* @param ServiceMethodBuilder $serviceMethodBuilder Used to construct a [@see ServiceMethod]
* @param Converter|StringConverter $converter Converter used to convert types before sending to service method
* @param int|null $index The position of the parameter or null if annotation does not reference parameter
* @return void
* @throws \InvalidArgumentException
*/
public function handle(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
?Converter $converter,
?int $index
): void {
if (!$converter instanceof StringConverter) {
throw new InvalidArgumentException(sprintf(
'Retrofit: Converter must be a StringConverter, %s found',
\gettype($converter)
));
}
$serviceMethodBuilder->addParameterHandler($index, new PathParamHandler($converter, $annotation->getValue()));
}
}
================================================
FILE: src/Internal/AnnotationHandler/QueryAnnotHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\AnnotationHandler;
use InvalidArgumentException;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\Retrofit\Annotation\Encodable;
use Tebru\Retrofit\Annotation\Query;
use Tebru\Retrofit\AnnotationHandler;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\Internal\ParameterHandler\QueryParamHandler;
use Tebru\Retrofit\ServiceMethodBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class QueryAnnotHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class QueryAnnotHandler implements AnnotationHandler
{
/**
* Add a query param handler
*
* @param Query|AbstractAnnotation $annotation The annotation to handle
* @param ServiceMethodBuilder $serviceMethodBuilder Used to construct a [@see ServiceMethod]
* @param Converter|StringConverter $converter Converter used to convert types before sending to service method
* @param int|null $index The position of the parameter or null if annotation does not reference parameter
* @return void
* @throws \InvalidArgumentException
*/
public function handle(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
?Converter $converter,
?int $index
): void {
if (!$annotation instanceof Encodable) {
throw new InvalidArgumentException('Retrofit: Annotation must be encodable');
}
if (!$converter instanceof StringConverter) {
throw new InvalidArgumentException(sprintf(
'Retrofit: Converter must be a StringConverter, %s found',
\gettype($converter)
));
}
$serviceMethodBuilder->addParameterHandler(
$index,
new QueryParamHandler($converter, $annotation->getValue(), $annotation->isEncoded())
);
}
}
================================================
FILE: src/Internal/AnnotationHandler/QueryMapAnnotHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\AnnotationHandler;
use InvalidArgumentException;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\Retrofit\Annotation\Encodable;
use Tebru\Retrofit\Annotation\QueryMap;
use Tebru\Retrofit\AnnotationHandler;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\Internal\ParameterHandler\QueryMapParamHandler;
use Tebru\Retrofit\ServiceMethodBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class QueryMapAnnotHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class QueryMapAnnotHandler implements AnnotationHandler
{
/**
* Add query map param handler
*
* @param QueryMap|AbstractAnnotation $annotation The annotation to handle
* @param ServiceMethodBuilder $serviceMethodBuilder Used to construct a [@see ServiceMethod]
* @param Converter|StringConverter $converter Converter used to convert types before sending to service method
* @param int|null $index The position of the parameter or null if annotation does not reference parameter
* @return void
* @throws \InvalidArgumentException
*/
public function handle(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
?Converter $converter,
?int $index
): void {
if (!$annotation instanceof Encodable) {
throw new InvalidArgumentException('Retrofit: Annotation must be encodable');
}
if (!$converter instanceof StringConverter) {
throw new InvalidArgumentException(sprintf(
'Retrofit: Converter must be a StringConverter, %s found',
\gettype($converter)
));
}
$serviceMethodBuilder->addParameterHandler(
$index,
new QueryMapParamHandler($converter, $annotation->isEncoded())
);
}
}
================================================
FILE: src/Internal/AnnotationHandler/QueryNameAnnotHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\AnnotationHandler;
use InvalidArgumentException;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\Retrofit\Annotation\Encodable;
use Tebru\Retrofit\Annotation\QueryName;
use Tebru\Retrofit\AnnotationHandler;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\Internal\ParameterHandler\QueryNameParamHandler;
use Tebru\Retrofit\ServiceMethodBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class QueryNameAnnotHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class QueryNameAnnotHandler implements AnnotationHandler
{
/**
* Add a query name param handler
*
* @param QueryName|AbstractAnnotation $annotation The annotation to handle
* @param ServiceMethodBuilder $serviceMethodBuilder Used to construct a [@see ServiceMethod]
* @param Converter|StringConverter $converter Converter used to convert types before sending to service method
* @param int|null $index The position of the parameter or null if annotation does not reference parameter
* @return void
* @throws \InvalidArgumentException
*/
public function handle(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
?Converter $converter,
?int $index
): void {
if (!$annotation instanceof Encodable) {
throw new InvalidArgumentException('Retrofit: Annotation must be encodable');
}
if (!$converter instanceof StringConverter) {
throw new InvalidArgumentException(sprintf(
'Retrofit: Converter must be a StringConverter, %s found',
\gettype($converter)
));
}
$serviceMethodBuilder->addParameterHandler(
$index,
new QueryNameParamHandler($converter, $annotation->isEncoded())
);
}
}
================================================
FILE: src/Internal/AnnotationHandler/UrlAnnotHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\AnnotationHandler;
use InvalidArgumentException;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\Retrofit\AnnotationHandler;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\Internal\ParameterHandler\UrlParamHandler;
use Tebru\Retrofit\ServiceMethodBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class UrlAnnotHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class UrlAnnotHandler implements AnnotationHandler
{
/**
* Add url param handler
*
* @param AbstractAnnotation $annotation The annotation to handle
* @param ServiceMethodBuilder $serviceMethodBuilder Used to construct a [@see ServiceMethod]
* @param Converter|StringConverter $converter Converter used to convert types before sending to service method
* @param int|null $index The position of the parameter or null if annotation does not reference parameter
* @return void
* @throws \InvalidArgumentException
*/
public function handle(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
?Converter $converter,
?int $index
): void {
if (!$converter instanceof StringConverter) {
throw new InvalidArgumentException(sprintf(
'Retrofit: Converter must be a StringConverter, %s found',
\gettype($converter)
));
}
$serviceMethodBuilder->addParameterHandler($index, new UrlParamHandler($converter));
}
}
================================================
FILE: src/Internal/AnnotationProcessor.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal;
use LogicException;
use ReflectionMethod;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\PhpType\TypeToken;
use Tebru\Retrofit\AnnotationHandler;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\Internal\Converter\ConverterProvider;
use Tebru\Retrofit\Annotation\ParameterAwareAnnotation;
use Tebru\Retrofit\RequestBodyConverter;
use Tebru\Retrofit\ServiceMethodBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class AnnotationProcessor
*
* Given an array of handlers, process an [@see AbstractAnnotation]
*
* @author Nate Brunette <n@tebru.net>
*/
final class AnnotationProcessor
{
/**
* An array of annotation handlers
*
* @var AnnotationHandler[]
*/
private $handlers;
/**
* Constructor
*
* @param AnnotationHandler[] $handlers
*/
public function __construct(array $handlers)
{
$this->handlers = $handlers;
}
/**
* Accepts an annotation and delegates to an [@see AnnotationHandler]
*
* @param AbstractAnnotation $annotation
* @param ServiceMethodBuilder $serviceMethodBuilder
* @param ConverterProvider $converterProvider
* @param ReflectionMethod $reflectionMethod
* @throws \LogicException
*/
public function process(
AbstractAnnotation $annotation,
ServiceMethodBuilder $serviceMethodBuilder,
ConverterProvider $converterProvider,
ReflectionMethod $reflectionMethod
): void {
$name = $annotation->getName();
if (!isset($this->handlers[$name])) {
return;
}
$handler = $this->handlers[$name];
$converter = null;
$index = null;
if ($annotation instanceof ParameterAwareAnnotation) {
$index = $this->findMethodParameterIndex($reflectionMethod, $annotation->getVariableName());
$type = $this->getParameterType($reflectionMethod, $index);
$converter = $this->getConverter($annotation, $converterProvider, $type);
}
$handler->handle($annotation, $serviceMethodBuilder, $converter, $index);
}
/**
* Find the position of the method parameter
*
* @param ReflectionMethod $reflectionMethod
* @param string $name
* @return int
* @throws \LogicException
*/
private function findMethodParameterIndex(ReflectionMethod $reflectionMethod, string $name): int
{
$reflectionParameters = $reflectionMethod->getParameters();
foreach ($reflectionParameters as $index => $reflectionParameter) {
if ($reflectionParameter->name === $name) {
return $index;
}
}
throw new LogicException(sprintf(
'Retrofit: Could not find parameter named %s in %s::%s. Please double check that annotations are properly ' .
'referencing method parameters.',
$name,
$reflectionMethod->getDeclaringClass()->name,
$reflectionMethod->name
));
}
/**
* Get the parameter type
*
* @param ReflectionMethod $reflectionMethod
* @param int $index
* @return TypeToken
* @throws \LogicException
*/
private function getParameterType(ReflectionMethod $reflectionMethod, int $index): TypeToken
{
$reflectionParameter = $reflectionMethod->getParameters()[$index];
$reflectionType = $reflectionParameter->getType();
if ($reflectionType === null) {
throw new LogicException(sprintf(
'Retrofit: Parameter type was not found for method %s::%s',
$reflectionMethod->getDeclaringClass()->name,
$reflectionMethod->name
));
}
/** @noinspection ExceptionsAnnotatingAndHandlingInspection */
return new TypeToken($reflectionType->getName());
}
/**
* Get the converter from annotation converter class
*
* @param ParameterAwareAnnotation $annotation
* @param ConverterProvider $converterProvider
* @param TypeToken $type
* @return Converter
* @throws \LogicException
*/
private function getConverter(
ParameterAwareAnnotation $annotation,
ConverterProvider $converterProvider,
TypeToken $type
): Converter {
switch ($annotation->converterType()) {
case RequestBodyConverter::class:
return $converterProvider->getRequestBodyConverter($type);
case StringConverter::class:
return $converterProvider->getStringConverter($type);
}
throw new LogicException(sprintf(
'Retrofit: Unable to handle converter of type %s. Please use RequestBodyConverter or StringConverter',
$annotation->converterType()
));
}
}
================================================
FILE: src/Internal/CacheProvider.php
================================================
<?php
declare(strict_types=1);
namespace Tebru\Retrofit\Internal;
use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\ChainAdapter;
use Symfony\Component\Cache\Adapter\NullAdapter;
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
use Symfony\Component\Cache\Adapter\Psr16Adapter;
use Symfony\Component\Cache\Exception\CacheException;
use Symfony\Component\Cache\Psr16Cache;
use Symfony\Component\Cache\Simple\ArrayCache;
use Symfony\Component\Cache\Simple\ChainCache;
use Symfony\Component\Cache\Simple\NullCache;
use Symfony\Component\Cache\Simple\PhpFilesCache;
/**
* @codeCoverageIgnore
*/
final class CacheProvider
{
/**
* Create a "file cache", chained to a "memory cache" depending on symfony/cache version
*
* @param string $cacheDir
* @return CacheInterface
* @throws CacheException
*/
public static function createFileCache(string $cacheDir): CacheInterface
{
// >= Symfony 4.3
if (class_exists('Symfony\Component\Cache\Psr16Cache')) {
return new Psr16Cache(new ChainAdapter([
new Psr16Adapter(self::createMemoryCache()),
new PhpFilesAdapter('', 0, $cacheDir),
]));
}
return new ChainCache([
self::createMemoryCache(),
new PhpFilesCache('', 0, $cacheDir)
]);
}
/**
* Create a "memory cache" depending on symfony/cache version
* @return CacheInterface
*/
public static function createMemoryCache(): CacheInterface
{
// >= Symfony 4.3
if (class_exists('Symfony\Component\Cache\Psr16Cache')) {
return new Psr16Cache(new ArrayAdapter(0, false));
}
return new ArrayCache(0, false);
}
/**
* Create a "null" cache (for annotations) depending on symfony/cache version
*
* @return CacheInterface
*/
public static function createNullCache(): CacheInterface
{
// >= Symfony 4.3
if (class_exists('Symfony\Component\Cache\Psr16Cache')) {
return new Psr16Cache(new NullAdapter());
}
return new NullCache();
}
}
================================================
FILE: src/Internal/CallAdapter/CallAdapterProvider.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\CallAdapter;
use LogicException;
use Tebru\PhpType\TypeToken;
use Tebru\Retrofit\CallAdapter;
use Tebru\Retrofit\CallAdapterFactory;
/**
* Class CallAdapterProvider
*
* Returns a [@see CallAdapterFactory] that can handle a given type
*
* @author Nate Brunette <n@tebru.net>
*/
final class CallAdapterProvider
{
/**
* @var CallAdapterFactory[]
*/
private $callAdapterFactories;
/**
* Constructor
*
* @param CallAdapterFactory[] $callAdapterFactories
*/
public function __construct(array $callAdapterFactories)
{
$this->callAdapterFactories = $callAdapterFactories;
}
/**
* Given a type, find the first available [@see CallAdapterFactory] and return it
*
* @param TypeToken $type
* @return CallAdapter
* @throws \LogicException
*/
public function get(TypeToken $type): CallAdapter
{
foreach ($this->callAdapterFactories as $callAdapterFactory) {
if ($callAdapterFactory->supports($type)) {
return $callAdapterFactory->create($type);
}
}
throw new LogicException(sprintf('Retrofit: Could not get call adapter for type %s', $type));
}
}
================================================
FILE: src/Internal/CallAdapter/DefaultCallAdapter.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\CallAdapter;
use Tebru\Retrofit\Call;
use Tebru\Retrofit\CallAdapter;
/**
* Class DefaultCallAdapter
*
* By default, do not alter a [@see Call]
*
* @author Nate Brunette <n@tebru.net>
*/
final class DefaultCallAdapter implements CallAdapter
{
/**
* Accepts a [@see Call] and converts it to the appropriate type
*
* @param Call $call
* @return Call
*/
public function adapt(Call $call): Call
{
return $call;
}
}
================================================
FILE: src/Internal/CallAdapter/DefaultCallAdapterFactory.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\CallAdapter;
use Tebru\PhpType\TypeToken;
use Tebru\Retrofit\Call;
use Tebru\Retrofit\CallAdapter;
use Tebru\Retrofit\CallAdapterFactory;
/**
* Class DefaultCallAdapterFactory
*
* Only supports [@see Call] instances
*
* @author Nate Brunette <n@tebru.net>
*/
final class DefaultCallAdapterFactory implements CallAdapterFactory
{
/**
* Returns true if the factory supports this type
*
* @param TypeToken $type
* @return bool
*/
public function supports(TypeToken $type): bool
{
return $type->isA(Call::class);
}
/**
* Create a new factory from type
*
* @param TypeToken $type
* @return CallAdapter
*/
public function create(TypeToken $type): CallAdapter
{
return new DefaultCallAdapter();
}
}
================================================
FILE: src/Internal/Converter/ConverterProvider.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\Converter;
use LogicException;
use Tebru\PhpType\TypeToken;
use Tebru\Retrofit\ConverterFactory;
use Tebru\Retrofit\RequestBodyConverter;
use Tebru\Retrofit\ResponseBodyConverter;
use Tebru\Retrofit\StringConverter;
/**
* Class ConverterProvider
*
* Returns a [@see ConverterFactory] that matches the provided type
*
* @author Nate Brunette <n@tebru.net>
*/
final class ConverterProvider
{
/**
* A cache of [@see ResponseBodyConverter]'s
*
* @var ResponseBodyConverter[]
*/
private $responseBodyConverters = [];
/**
* A cache of [@see RequestBodyConverter]'s
*
* @var RequestBodyConverter[]
*/
private $requestBodyConverters = [];
/**
* A cache of [@see StringConverter]'s
*
* @var StringConverter[]
*/
private $stringConverters = [];
/**
* An array of [@see ConverterFactory]'s
*
* @var ConverterFactory[]
*/
private $converterFactories;
/**
* Constructor
*
* @param ConverterFactory[] $factories
*/
public function __construct(array $factories)
{
$this->converterFactories = array_values($factories);
}
/**
* Get a response body converter for type
*
* @param TypeToken $type
* @return ResponseBodyConverter
* @throws LogicException
*/
public function getResponseBodyConverter(TypeToken $type): ResponseBodyConverter
{
$key = (string)$type;
if (isset($this->responseBodyConverters[$key])) {
return $this->responseBodyConverters[$key];
}
foreach ($this->converterFactories as $converterFactory) {
$converter = $converterFactory->responseBodyConverter($type);
if ($converter === null) {
continue;
}
$this->responseBodyConverters[$key] = $converter;
return $converter;
}
throw new LogicException(sprintf(
'Retrofit: Could not get response body converter for type %s',
$type
));
}
/**
* Get a request body converter for type
*
* @param TypeToken $type
* @return RequestBodyConverter
* @throws \LogicException
*/
public function getRequestBodyConverter(TypeToken $type): RequestBodyConverter
{
$key = (string)$type;
if (isset($this->requestBodyConverters[$key])) {
return $this->requestBodyConverters[$key];
}
foreach ($this->converterFactories as $converterFactory) {
$converter = $converterFactory->requestBodyConverter($type);
if ($converter === null) {
continue;
}
$this->requestBodyConverters[$key] = $converter;
return $converter;
}
throw new LogicException(sprintf(
'Retrofit: Could not get request body converter for type %s',
$type
));
}
/**
* Get a string converter for type
*
* @param TypeToken $type
* @return StringConverter
* @throws \LogicException
*/
public function getStringConverter(TypeToken $type): StringConverter
{
$key = (string)$type;
if (isset($this->stringConverters[$key])) {
return $this->stringConverters[$key];
}
foreach ($this->converterFactories as $converterFactory) {
$converter = $converterFactory->stringConverter($type);
if ($converter === null) {
continue;
}
$this->stringConverters[$key] = $converter;
return $converter;
}
throw new LogicException(sprintf(
'Retrofit: Could not get string converter for type %s',
$type
));
}
}
================================================
FILE: src/Internal/Converter/DefaultConverterFactory.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\Converter;
use Psr\Http\Message\StreamInterface;
use Tebru\PhpType\TypeToken;
use Tebru\Retrofit\ConverterFactory;
use Tebru\Retrofit\RequestBodyConverter;
use Tebru\Retrofit\ResponseBodyConverter;
use Tebru\Retrofit\StringConverter;
/**
* Class DefaultConverterFactory
*
* Creates default converters
*
* @author Nate Brunette <n@tebru.net>
*/
final class DefaultConverterFactory implements ConverterFactory
{
/**
* Return default converter if type is a stream
*
* @param TypeToken $type
* @return null|ResponseBodyConverter
*/
public function responseBodyConverter(TypeToken $type): ?ResponseBodyConverter
{
if (!$type->isA(StreamInterface::class)) {
return null;
}
return new DefaultResponseBodyConverter();
}
/**
* Return default converter if type is a stream
*
* @param TypeToken $type
* @return null|RequestBodyConverter
*/
public function requestBodyConverter(TypeToken $type): ?RequestBodyConverter
{
if (!$type->isA(StreamInterface::class)) {
return null;
}
return new DefaultRequestBodyConverter();
}
/**
* Return default string converter for any type
*
* If the type is a string already, use a converter that doesn't do
* any type checking.
*
* @param TypeToken $type
* @return null|StringConverter
*/
public function stringConverter(TypeToken $type): ?StringConverter
{
if ($type->isString()) {
return new NoopStringConverter();
}
return new DefaultStringConverter();
}
}
================================================
FILE: src/Internal/Converter/DefaultRequestBodyConverter.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\Converter;
use Psr\Http\Message\StreamInterface;
use Tebru\Retrofit\RequestBodyConverter;
/**
* Class DefaultRequestBodyConverter
*
* Noop, defaults to returning stream
*
* @author Nate Brunette <n@tebru.net>
*/
final class DefaultRequestBodyConverter implements RequestBodyConverter
{
/**
* The value here should already be a stream, so we can return it
*
* @param mixed $value
* @return StreamInterface
*/
public function convert($value): StreamInterface
{
return $value;
}
}
================================================
FILE: src/Internal/Converter/DefaultResponseBodyConverter.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\Converter;
use Psr\Http\Message\StreamInterface;
use Tebru\Retrofit\ResponseBodyConverter;
/**
* Class DefaultResponseBodyConverter
*
* Noop, returns response body stream
*
* @author Nate Brunette <n@tebru.net>
*/
final class DefaultResponseBodyConverter implements ResponseBodyConverter
{
/**
* By default, returns the stream
*
* @param StreamInterface $value
* @return StreamInterface
*/
public function convert(StreamInterface $value): StreamInterface
{
return $value;
}
}
================================================
FILE: src/Internal/Converter/DefaultStringConverter.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\Converter;
use Tebru\Retrofit\StringConverter;
/**
* Class DefaultStringConverter
*
* @author Nate Brunette <n@tebru.net>
*/
final class DefaultStringConverter implements StringConverter
{
/**
* Convert any supported value to a string
*
* @param mixed $value
* @return string
*/
public function convert($value): string
{
// if it's an array or object, just serialize it
if (\is_array($value) || \is_object($value)) {
return serialize($value);
}
if ($value === true) {
return 'true';
}
if ($value === false) {
return 'false';
}
return (string)$value;
}
}
================================================
FILE: src/Internal/Converter/NoopStringConverter.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\Converter;
use Tebru\Retrofit\StringConverter;
/**
* Class NoopStringConverter
*
* @author Nate Brunette <n@tebru.net>
*/
class NoopStringConverter implements StringConverter
{
/**
* Only types that are known to be strings should be passed to this converter,
* so we can just return the value.
*
* @param string $value
* @return string
*/
public function convert($value): string
{
return $value;
}
}
================================================
FILE: src/Internal/DefaultProxyFactory.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal;
use InvalidArgumentException;
use LogicException;
use PhpParser\BuilderFactory;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name;
use PhpParser\Node\NullableType;
use PhpParser\Node\Scalar\DNumber;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Return_;
use PhpParser\PrettyPrinterAbstract;
use ReflectionClass;
use RuntimeException;
use Tebru\PhpType\TypeToken;
use Tebru\Retrofit\HttpClient;
use Tebru\Retrofit\Internal\ServiceMethod\ServiceMethodFactory;
use Tebru\Retrofit\Proxy;
use Tebru\Retrofit\Proxy\AbstractProxy;
use Tebru\Retrofit\ProxyFactory;
/**
* Class DefaultProxyFactory
*
* @author Nate Brunette <n@tebru.net>
*/
final class DefaultProxyFactory implements ProxyFactory
{
public const PROXY_PREFIX = 'Tebru\Retrofit\Proxy\\';
/**
* @var BuilderFactory
*/
private $builderFactory;
/**
* @var PrettyPrinterAbstract
*/
private $printer;
/**
* @var ServiceMethodFactory
*/
private $serviceMethodFactory;
/**
* @var HttpClient
*/
private $httpClient;
/**
* @var Filesystem
*/
private $filesystem;
/**
* @var bool
*/
private $enableCache;
/**
* @var string
*/
private $cacheDir;
/**
* Constructor
*
* @param BuilderFactory $builderFactory
* @param PrettyPrinterAbstract $printer
* @param ServiceMethodFactory $serviceMethodFactory
* @param HttpClient $httpClient
* @param Filesystem $filesystem
* @param bool $enableCache
* @param string $cacheDir
*/
public function __construct(
BuilderFactory $builderFactory,
PrettyPrinterAbstract $printer,
ServiceMethodFactory $serviceMethodFactory,
HttpClient $httpClient,
Filesystem $filesystem,
bool $enableCache,
string $cacheDir
) {
$this->builderFactory = $builderFactory;
$this->printer = $printer;
$this->serviceMethodFactory = $serviceMethodFactory;
$this->httpClient = $httpClient;
$this->filesystem = $filesystem;
$this->enableCache = $enableCache;
$this->cacheDir = $cacheDir;
}
/**
* Create a new proxy class given an interface name. This returns a class
* in a string to be cached.
*
* @param string $service
* @return Proxy
* @throws \RuntimeException
* @throws \LogicException
* @throws \Tebru\PhpType\Exception\MalformedTypeException
* @throws \InvalidArgumentException
*/
public function create(string $service): ?Proxy
{
$className = self::PROXY_PREFIX.$service;
if ($this->enableCache && class_exists($className)) {
return new $className($this->serviceMethodFactory, $this->httpClient);
}
if (!$this->enableCache && class_exists($className, false)) {
return new $className($this->serviceMethodFactory, $this->httpClient);
}
if (!interface_exists($service)) {
throw new InvalidArgumentException(sprintf('Retrofit: %s is expected to be an interface', $service));
}
/** @noinspection ExceptionsAnnotatingAndHandlingInspection */
$reflectionClass = new ReflectionClass($service);
$builder = $this->builderFactory
->class($reflectionClass->getShortName())
->extend('\\'.AbstractProxy::class)
->implement('\\'.$reflectionClass->name);
foreach ($reflectionClass->getMethods() as $reflectionMethod) {
$methodBuilder = $this->builderFactory
->method($reflectionMethod->name)
->makePublic();
if ($reflectionMethod->isStatic()) {
$methodBuilder->makeStatic();
}
$defaultValues = [];
foreach ($reflectionMethod->getParameters() as $reflectionParameter) {
$paramBuilder = $this->builderFactory->param($reflectionParameter->name);
if ($reflectionParameter->isDefaultValueAvailable()) {
$paramBuilder->setDefault($reflectionParameter->getDefaultValue());
}
if ($reflectionParameter->getType() === null) {
throw new LogicException(sprintf(
'Retrofit: Parameter types are required. None found for parameter %s in %s::%s()',
$reflectionParameter->name,
$reflectionClass->name,
$reflectionMethod->name
));
}
$reflectionTypeName = $reflectionParameter->getType()->getName();
if ((new TypeToken($reflectionTypeName))->isObject()) {
$reflectionTypeName = '\\'.$reflectionTypeName;
}
$type = $reflectionParameter->getType()->allowsNull() ? new NullableType($reflectionTypeName): $reflectionTypeName;
$paramBuilder->setTypeHint($type);
if ($reflectionParameter->isPassedByReference()) {
$paramBuilder->makeByRef();
}
if ($reflectionParameter->isVariadic()) {
$paramBuilder->makeVariadic();
}
$methodBuilder->addParam($paramBuilder->getNode());
// set all default values
// if a method is called with two few arguments, a native exception will be thrown
// so we can safely use null as a placeholder here.
$defaultValues[] = $reflectionParameter->isDefaultValueAvailable()
? $reflectionParameter->getDefaultValue()
: null;
}
if (!$reflectionMethod->hasReturnType()) {
throw new LogicException(sprintf(
'Retrofit: Method return types are required. None found for %s::%s()',
$reflectionClass->name,
$reflectionMethod->name
));
}
/** @noinspection NullPointerExceptionInspection */
$methodBuilder->setReturnType('\\'.$reflectionMethod->getReturnType()->getName());
$defaultNodes = $this->mapArray($defaultValues);
$methodBuilder->addStmt(
new Return_(
new MethodCall(
new Variable('this'),
'__handleRetrofitRequest',
[
new String_($reflectionClass->name),
new ConstFetch(new Name('__FUNCTION__')),
new FuncCall(new Name('func_get_args')),
new Array_($defaultNodes)
]
)
)
);
$builder->addStmt($methodBuilder->getNode());
}
$namespaceBuilder = $this->builderFactory
->namespace(self::PROXY_PREFIX.$reflectionClass->getNamespaceName())
->addStmt($builder);
$source = $this->printer->prettyPrint([$namespaceBuilder->getNode()]);
eval($source);
if (!$this->enableCache) {
return new $className($this->serviceMethodFactory, $this->httpClient);
}
$directory = $this->cacheDir.DIRECTORY_SEPARATOR.$reflectionClass->getNamespaceName();
$directory = str_replace('\\', DIRECTORY_SEPARATOR, $directory);
$filename = $directory.DIRECTORY_SEPARATOR.$reflectionClass->getShortName().'.php';
$class = '<?php'.PHP_EOL.PHP_EOL.$source;
if (!$this->filesystem->makeDirectory($directory)) {
throw new RuntimeException(
sprintf(
'Retrofit: There was an issue creating the cache directory: %s',
$directory
)
);
}
if (!$this->filesystem->put($filename, $class)) {
throw new RuntimeException(sprintf('Retrofit: There was an issue writing proxy class to: %s', $filename));
}
return new $className($this->serviceMethodFactory, $this->httpClient);
}
/**
* Convert array to an array of [@see Expr] to add to builder
*
* @param array $array
* @return Expr[]
*/
private function mapArray(array $array): array
{
// for each element in the array, create an Expr object
$values = array_values(array_map(function ($value) {
$type = TypeToken::createFromVariable($value);
switch ($type) {
case TypeToken::STRING:
return new String_($value);
case TypeToken::INTEGER:
return new LNumber($value);
case TypeToken::FLOAT:
return new DNumber($value);
case TypeToken::BOOLEAN:
return $value === true ? new ConstFetch(new Name('true')) : new ConstFetch(new Name('false'));
case TypeToken::HASH:
// recurse if array contains an array
return new Array_($this->mapArray($value));
case TypeToken::NULL:
return new ConstFetch(new Name('null'));
}
}, $array));
$keys = \array_keys($array);
$isNumericKeys = \count(\array_filter($keys, '\is_string')) === 0;
// a 0-indexed array can be returned as-is
if ($isNumericKeys) {
return $values;
}
// if we're dealing with an associative array, run the keys through the mapper
$keys = $this->mapArray($keys);
// create an array of ArrayItem objects for an associative array
$items = [];
foreach ($values as $index => $value) {
$items[] = new Expr\ArrayItem($value, $keys[$index]);
}
return $items;
}
}
================================================
FILE: src/Internal/Filesystem.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal;
/**
* A light wrapper around filesystem commands
*
* @author Nate Brunette <n@tebru.net>
*/
class Filesystem
{
/**
* Wraps the php mkdir() function, but defaults to recursive directory creation
*
* @param string $pathname
* @param int $mode
* @param bool $recursive
* @return bool
*/
public function makeDirectory(string $pathname, int $mode = 0777, bool $recursive = true): bool
{
return !(!@mkdir($pathname, $mode, $recursive) && !is_dir($pathname));
}
/**
* Write contents to file
*
* @param string $filename
* @param string $contents
* @return bool
*/
public function put(string $filename, string $contents): bool
{
$written = file_put_contents($filename, $contents);
return !($written === 0);
}
}
================================================
FILE: src/Internal/HttpClientCall.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Tebru\Retrofit\Call;
use Tebru\Retrofit\Exception\ResponseHandlingFailedException;
use Tebru\Retrofit\HttpClient;
use Tebru\Retrofit\Response;
use Throwable;
/**
* Class HttpClientCall
*
* @author Nate Brunette <n@tebru.net>
*/
final class HttpClientCall implements Call
{
/**
* A retrofit http client implementation
*
* @var HttpClient
*/
private $client;
/**
* A web service resource as a method model
*
* @var ServiceMethod
*/
private $serviceMethod;
/**
* The runtime arguments that a request should be constructed with
*
* @var array
*/
private $args;
/**
* The constructed request
*
* @var RequestInterface
*/
private $request;
/**
* Constructor
*
* @param HttpClient $client
* @param ServiceMethod $serviceMethod
* @param array $args
*/
public function __construct(HttpClient $client, ServiceMethod $serviceMethod, array $args)
{
$this->client = $client;
$this->serviceMethod = $serviceMethod;
$this->args = $args;
}
/**
* Execute request synchronously
*
* A [@see Response] will be returned
*
* @return Response
*/
public function execute(): Response
{
$response = $this->client->send($this->request());
return $this->createResponse($response);
}
/**
* Execute request asynchronously
*
* This method accepts two optional callbacks.
*
* onResponse() will be called for any request that gets a response,
* whether it was successful or not. It will send a [@see Response] as
* the parameter.
*
* onFailure() will be called in the event a network request failed. It
* will send the [@see Throwable] that was encountered.
*
* Example of method signatures:
*
* $call->enqueue(
* function (\Tebru\Retrofit\Call $call, \Tebru\Retrofit\Response $response) {},
* function (\Throwable $throwable) {}
* );
*
* @param callable $onResponse On any response
* @param callable $onFailure On any network request failure
* @return Call
* @throws \LogicException
*/
public function enqueue(?callable $onResponse = null, ?callable $onFailure = null): Call
{
$this->client->sendAsync(
$this->request(),
function (ResponseInterface $response) use ($onResponse) {
if ($onResponse !== null) {
$onResponse($this->createResponse($response));
}
},
function (Throwable $throwable) use ($onFailure) {
if ($onFailure === null) {
throw $throwable;
}
$onFailure($throwable);
}
);
return $this;
}
/**
* When making requests asynchronously, call wait() to execute the requests
*
* @return void
*/
public function wait(): void
{
$this->client->wait();
}
/**
* Get the PSR-7 request
*
* @return RequestInterface
*/
public function request(): RequestInterface
{
if ($this->request === null) {
$this->request = $this->serviceMethod->toRequest($this->args);
}
return $this->request;
}
/**
* Create a [@see Response] from a PSR-7 response
*
* @param ResponseInterface $response
* @return RetrofitResponse
*/
private function createResponse(ResponseInterface $response): RetrofitResponse
{
$code = $response->getStatusCode();
if ($code >= 200 && $code < 300) {
try {
$responseBody = $this->serviceMethod->toResponseBody($response);
} catch (Throwable $throwable) {
throw new ResponseHandlingFailedException(
$this->request(),
$response,
'Retrofit: Could not convert response body',
$throwable
);
}
return new RetrofitResponse($response, $responseBody, null);
}
try {
$errorBody = $this->serviceMethod->toErrorBody($response);
} catch (Throwable $throwable) {
throw new ResponseHandlingFailedException(
$this->request(),
$response,
'Retrofit: Could not convert error body',
$throwable
);
}
return new RetrofitResponse($response, null, $errorBody);
}
}
================================================
FILE: src/Internal/ParameterHandler/AbstractParameterHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ParameterHandler;
use Generator;
use RuntimeException;
use Tebru\Retrofit\Http\MultipartBody;
use Tebru\Retrofit\ParameterHandler;
use Tebru\Retrofit\Internal\RequestBuilder;
use Tebru\Retrofit\RequestBodyConverter;
/**
* Class AbstractFieldHandler
*
* @author Nate Brunette <n@tebru.net>
*/
abstract class AbstractParameterHandler implements ParameterHandler
{
private const HEADER_CON_TRANS_ENC = 'Content-Transfer-Encoding';
/**
* Convert a value to a generator
*
* This method is used when a value can optionally be an array and each element in the
* array should be processed the same way.
*
* @param array|mixed $list
* @return Generator
* @throws \RuntimeException
*/
protected function getListValues($list): Generator
{
foreach ((array)$list as $key => $element) {
if (!\is_int($key)) {
throw new RuntimeException('Retrofit: Array value must use numeric keys');
}
yield $element;
}
}
/**
* Handle Part or PartMap annotations
*
* This could use a simple method using name and value, or if a [@see MultipartBody] is passed in as the
* value, then a filename and additional headers could be set as well.
*
* @param RequestBuilder $requestBuilder
* @param RequestBodyConverter $converter
* @param string $name
* @param mixed $value
* @param string $encoding
* @return void
*/
protected function handlePart(
RequestBuilder $requestBuilder,
RequestBodyConverter $converter,
string $name,
$value,
string $encoding
): void {
if ($value === null) {
return;
}
// if not a MultipartBody, only set name, contents, and content header
if (!$value instanceof MultipartBody) {
$requestBuilder->addPart($name, $converter->convert($value), [self::HEADER_CON_TRANS_ENC => $encoding]);
return;
}
$headers = $value->getHeaders();
if (!isset($headers[self::HEADER_CON_TRANS_ENC])) {
$headers[self::HEADER_CON_TRANS_ENC] = $encoding;
}
$requestBuilder->addPart($value->getName(), $value->getContents(), $headers, $value->getFilename());
}
}
================================================
FILE: src/Internal/ParameterHandler/BodyParamHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ParameterHandler;
use Tebru\Retrofit\Internal\RequestBuilder;
use Tebru\Retrofit\RequestBodyConverter;
/**
* Class BodyParamHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class BodyParamHandler extends AbstractParameterHandler
{
/**
* @var RequestBodyConverter
*/
private $converter;
/**
* Constructor
*
* @param RequestBodyConverter $converter
*/
public function __construct(RequestBodyConverter $converter)
{
$this->converter = $converter;
}
/**
* Converts the value to a stream, then sets the body to the request builder
*
* @param RequestBuilder $requestBuilder
* @param mixed $value
* @return void
*/
public function apply(RequestBuilder $requestBuilder, $value): void
{
if ($value === null) {
return;
}
$requestBuilder->setBody($this->converter->convert($value));
}
}
================================================
FILE: src/Internal/ParameterHandler/FieldMapParamHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ParameterHandler;
use Iterator;
use Tebru\Retrofit\Internal\RequestBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class FieldMapParamHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class FieldMapParamHandler extends AbstractParameterHandler
{
/**
* @var StringConverter
*/
private $converter;
/**
* @var bool
*/
private $encoded;
/**
* Constructor
*
* @param StringConverter $converter
* @param bool $encoded
*/
public function __construct(StringConverter $converter, bool $encoded)
{
$this->converter = $converter;
$this->encoded = $encoded;
}
/**
* Set a value to the [@see RequestBuilder] for parameter type
*
* @param RequestBuilder $requestBuilder
* @param array|Iterator $map
* @throws \RuntimeException
*/
public function apply(RequestBuilder $requestBuilder, $map): void
{
if ($map === null) {
return;
}
foreach ($map as $name => $value) {
foreach ($this->getListValues($value) as $element) {
$requestBuilder->addField($name, $this->converter->convert($element), $this->encoded);
}
}
}
}
================================================
FILE: src/Internal/ParameterHandler/FieldParamHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ParameterHandler;
use Tebru\Retrofit\Internal\RequestBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class FieldParamHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class FieldParamHandler extends AbstractParameterHandler
{
/**
* @var StringConverter
*/
private $converter;
/**
* @var string
*/
private $name;
/**
* @var bool
*/
private $encoded;
/**
* Constructor
*
* @param string $name
* @param StringConverter $converter
* @param bool $encoded
*/
public function __construct(StringConverter $converter, string $name, bool $encoded)
{
$this->converter = $converter;
$this->name = $name;
$this->encoded = $encoded;
}
/**
* Set a value to the [@see RequestBuilder] for parameter type
*
* @param RequestBuilder $requestBuilder
* @param mixed $value
* @return void
* @throws \RuntimeException
*/
public function apply(RequestBuilder $requestBuilder, $value): void
{
if ($value === null) {
return;
}
foreach ($this->getListValues($value) as $element) {
$requestBuilder->addField($this->name, $this->converter->convert($element), $this->encoded);
}
}
}
================================================
FILE: src/Internal/ParameterHandler/HeaderMapParamHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ParameterHandler;
use Iterator;
use Tebru\Retrofit\Internal\RequestBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class HeaderMapParamHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class HeaderMapParamHandler extends AbstractParameterHandler
{
/**
* @var StringConverter
*/
private $converter;
/**
* Constructor
*
* @param StringConverter $converter
*/
public function __construct(StringConverter $converter)
{
$this->converter = $converter;
}
/**
* Set a value to the [@see RequestBuilder] for parameter type
*
* @param RequestBuilder $requestBuilder
* @param array|Iterator $map
* @return void
* @throws \RuntimeException
*/
public function apply(RequestBuilder $requestBuilder, $map): void
{
if ($map === null) {
return;
}
foreach ($map as $name => $value) {
foreach ($this->getListValues($value) as $element) {
$requestBuilder->addHeader($name, $this->converter->convert($element));
}
}
}
}
================================================
FILE: src/Internal/ParameterHandler/HeaderParamHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ParameterHandler;
use Tebru\Retrofit\Internal\RequestBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class HeaderParamHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class HeaderParamHandler extends AbstractParameterHandler
{
/**
* @var StringConverter
*/
private $converter;
/**
* @var string
*/
private $name;
/**
* Constructor
*
* @param StringConverter $converter
* @param string $name
*/
public function __construct(StringConverter $converter, string $name)
{
$this->converter = $converter;
$this->name = $name;
}
/**
* Set a value to the [@see RequestBuilder] for parameter type
*
* @param RequestBuilder $requestBuilder
* @param mixed $value
* @return void
* @throws \RuntimeException
*/
public function apply(RequestBuilder $requestBuilder, $value): void
{
if ($value === null) {
return;
}
foreach ($this->getListValues($value) as $element) {
$requestBuilder->addHeader($this->name, $this->converter->convert($element));
}
}
}
================================================
FILE: src/Internal/ParameterHandler/PartMapParamHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ParameterHandler;
use Iterator;
use Tebru\Retrofit\Internal\RequestBuilder;
use Tebru\Retrofit\RequestBodyConverter;
/**
* Class PartMapParamHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class PartMapParamHandler extends AbstractParameterHandler
{
/**
* @var RequestBodyConverter
*/
private $converter;
/**
* @var string
*/
private $encoding;
/**
* Constructor
*
* @param RequestBodyConverter $converter
* @param string $encoding
*/
public function __construct(RequestBodyConverter $converter, string $encoding)
{
$this->converter = $converter;
$this->encoding = $encoding;
}
/**
* Set a value to the [@see RequestBuilder] for parameter type
*
* @param RequestBuilder $requestBuilder
* @param array|Iterator $map
* @return void
*/
public function apply(RequestBuilder $requestBuilder, $map): void
{
if ($map === null) {
return;
}
foreach ($map as $name => $value) {
$this->handlePart($requestBuilder, $this->converter, $name, $value, $this->encoding);
}
}
}
================================================
FILE: src/Internal/ParameterHandler/PartParamHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ParameterHandler;
use Tebru\Retrofit\Internal\RequestBuilder;
use Tebru\Retrofit\RequestBodyConverter;
/**
* Class PartParamHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class PartParamHandler extends AbstractParameterHandler
{
/**
* @var RequestBodyConverter
*/
private $converter;
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $encoding;
/**
* Constructor
*
* @param RequestBodyConverter $converter
* @param string $name
* @param string $encoding
*/
public function __construct(RequestBodyConverter $converter, string $name, string $encoding)
{
$this->converter = $converter;
$this->name = $name;
$this->encoding = $encoding;
}
/**
* Set a value to the [@see RequestBuilder] for parameter type
*
* @param RequestBuilder $requestBuilder
* @param mixed $value
* @return void
*/
public function apply(RequestBuilder $requestBuilder, $value): void
{
if ($value === null) {
return;
}
$this->handlePart($requestBuilder, $this->converter, $this->name, $value, $this->encoding);
}
}
================================================
FILE: src/Internal/ParameterHandler/PathParamHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ParameterHandler;
use RuntimeException;
use Tebru\Retrofit\Internal\RequestBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class PathParamHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class PathParamHandler extends AbstractParameterHandler
{
/**
* @var StringConverter
*/
private $converter;
/**
* @var string
*/
private $name;
/**
* Constructor
*
* @param StringConverter $converter
* @param string $name
*/
public function __construct(StringConverter $converter, string $name)
{
$this->converter = $converter;
$this->name = $name;
}
/**
* Set a value to the [@see RequestBuilder] for parameter type
*
* @param RequestBuilder $requestBuilder
* @param mixed $value
* @return void
* @throws \RuntimeException
*/
public function apply(RequestBuilder $requestBuilder, $value): void
{
if ($value === null) {
throw new RuntimeException('Path parameters cannot be null');
}
$requestBuilder->replacePath($this->name, $this->converter->convert($value));
}
}
================================================
FILE: src/Internal/ParameterHandler/QueryMapParamHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ParameterHandler;
use Iterator;
use Tebru\Retrofit\Internal\RequestBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class QueryMapParamHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class QueryMapParamHandler extends AbstractParameterHandler
{
/**
* @var StringConverter
*/
private $converter;
/**
* @var bool
*/
private $encoded;
/**
* Constructor
*
* @param StringConverter $converter
* @param bool $encoded
*/
public function __construct(StringConverter $converter, bool $encoded)
{
$this->converter = $converter;
$this->encoded = $encoded;
}
/**
* Set a value to the [@see RequestBuilder] for parameter type
*
* @param RequestBuilder $requestBuilder
* @param array|Iterator $map
* @return void
* @throws \RuntimeException
*/
public function apply(RequestBuilder $requestBuilder, $map): void
{
if ($map === null) {
return;
}
foreach ($map as $name => $value) {
foreach ($this->getListValues($value) as $element) {
$requestBuilder->addQuery($name, $this->converter->convert($element), $this->encoded);
}
}
}
}
================================================
FILE: src/Internal/ParameterHandler/QueryNameParamHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ParameterHandler;
use Tebru\Retrofit\Internal\RequestBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class QueryNameParamHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class QueryNameParamHandler extends AbstractParameterHandler
{
/**
* @var StringConverter
*/
private $converter;
/**
* @var bool
*/
private $encoded;
/**
* Constructor
*
* @param StringConverter $converter
* @param bool $encoded
*/
public function __construct(StringConverter $converter, bool $encoded)
{
$this->converter = $converter;
$this->encoded = $encoded;
}
/**
* Set a value to the [@see RequestBuilder] for parameter type
*
* @param RequestBuilder $requestBuilder
* @param mixed $value
* @return void
* @throws \RuntimeException
*/
public function apply(RequestBuilder $requestBuilder, $value): void
{
if ($value === null) {
return;
}
foreach ($this->getListValues($value) as $element) {
$requestBuilder->addQueryName($this->converter->convert($element), $this->encoded);
}
}
}
================================================
FILE: src/Internal/ParameterHandler/QueryParamHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ParameterHandler;
use Tebru\Retrofit\Internal\RequestBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class QueryParamHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class QueryParamHandler extends AbstractParameterHandler
{
/**
* @var StringConverter
*/
private $converter;
/**
* @var string
*/
private $name;
/**
* @var bool
*/
private $encoded;
/**
* Constructor
*
* @param string $name
* @param StringConverter $converter
* @param bool $encoded
*/
public function __construct(StringConverter $converter, string $name, bool $encoded)
{
$this->converter = $converter;
$this->name = $name;
$this->encoded = $encoded;
}
/**
* Set a value to the [@see RequestBuilder] for parameter type
*
* @param RequestBuilder $requestBuilder
* @param mixed $value
* @return void
* @throws \RuntimeException
*/
public function apply(RequestBuilder $requestBuilder, $value): void
{
if ($value === null) {
return;
}
foreach ($this->getListValues($value) as $element) {
$requestBuilder->addQuery($this->name, $this->converter->convert($element), $this->encoded);
}
}
}
================================================
FILE: src/Internal/ParameterHandler/UrlParamHandler.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ParameterHandler;
use RuntimeException;
use Tebru\Retrofit\Internal\RequestBuilder;
use Tebru\Retrofit\StringConverter;
/**
* Class UrlParamHandler
*
* @author Nate Brunette <n@tebru.net>
*/
final class UrlParamHandler extends AbstractParameterHandler
{
/**
* @var StringConverter
*/
private $converter;
/**
* Constructor
*
* @param StringConverter $converter
*/
public function __construct(StringConverter $converter)
{
$this->converter = $converter;
}
/**
* Set a value to the [@see RequestBuilder] for parameter type
*
* @param RequestBuilder $requestBuilder
* @param mixed $value
* @return void
* @throws \RuntimeException
*/
public function apply(RequestBuilder $requestBuilder, $value): void
{
if ($value === null) {
throw new RuntimeException('Url parameters cannot be null');
}
$requestBuilder->setBaseUrl($this->converter->convert($value));
}
}
================================================
FILE: src/Internal/RequestBuilder.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\MultipartStream;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Uri;
use LogicException;
use Psr\Http\Message\StreamInterface;
/**
* Class RequestBuilder
*
* @author Nate Brunette <n@tebru.net>
*/
final class RequestBuilder
{
/**
* The request method
*
* @var string
*/
private $method;
/**
* The request [@see Uri] object
*
* @var Uri
*/
private $uri;
/**
* An array of query strings that can be appended together
*
* @var array
*/
private $queries = [];
/**
* An array of headers in PSR-7 format
*
* @var array
*/
private $headers;
/**
* The request body
*
* @var StreamInterface
*/
private $body;
/**
* An array of request body fields that can be appended together
*
* @var array
*/
private $fields = [];
/**
* An array of arrays of multipart parts, each with name, content, headers, and filename
*
* @var array[]
*/
private $parts = [];
/**
* Constructor
*
* @param string $method
* @param string $baseUrl
* @param string $uri
* @param array $headers
*/
public function __construct(string $method, string $baseUrl, string $uri, array $headers)
{
$this->method = $method;
$this->uri = new Uri($baseUrl.$uri);
$this->headers = $headers;
}
/**
* Set the uri base url
*
* @param string $value
*/
public function setBaseUrl(string $value): void
{
$uri = new Uri($value);
$this->uri = $this->uri
->withScheme($uri->getScheme())
->withHost($uri->getHost())
->withPort($uri->getPort());
}
/**
* Replace a url path placeholder with a value
*
* @param string $name
* @param string $value
*/
public function replacePath(string $name, string $value): void
{
$path = rawurldecode($this->uri->getPath());
$path = str_replace(sprintf('{%s}', $name), $value, $path);
$this->uri = $this->uri->withPath($path);
}
/**
* Add a query string; if encoded, decodes to be encoded later
*
* @param string $name
* @param string $value
* @param bool $encoded
*/
public function addQuery(string $name, string $value, bool $encoded): void
{
$name = rawurlencode($name);
if ($encoded === false) {
$value = rawurlencode($value);
}
$this->queries[] = $name.'='.$value;
}
/**
* Adds a query string without value; if encoded, decodes to be encoded later
*
* @param string $value
* @param bool $encoded
*/
public function addQueryName(string $value, bool $encoded): void
{
if ($encoded === false) {
$value = rawurlencode($value);
}
$this->queries[] = $value;
}
/**
* Add a header in PSR-7 format
*
* @param string $name
* @param string $value
*/
public function addHeader(string $name, string $value): void
{
$name = strtolower($name);
$this->headers[$name][] = $value;
}
/**
* Set the request body
*
* @param StreamInterface $body
*/
public function setBody(StreamInterface $body): void
{
$this->body = $body;
}
/**
* Add a field; if not encoded, encodes first
*
* @param string $name
* @param string $value
* @param bool $encoded
*/
public function addField(string $name, string $value, bool $encoded): void
{
$name = rawurlencode($name);
if ($encoded === false) {
$value = rawurlencode($value);
}
$this->fields[] = $name.'='.$value;
}
/**
* Add a multipart part
*
* @param string $name
* @param StreamInterface $contents
* @param array $headers
* @param null|string $filename
*/
public function addPart(string $name, StreamInterface $contents, array $headers = [], ?string $filename = null): void
{
$this->parts[] = [
'name' => $name,
'contents' => $contents,
'headers' => $headers,
'filename' => $filename,
];
}
/**
* Create a PSR-7 request
*
* @return Request
* @throws \LogicException
*/
public function build(): Request
{
$uri = $this->uri;
if ($this->queries !== []) {
$query = implode('&', $this->queries);
$uri = $this->uri->getQuery() === ''
? $this->uri->withQuery($query)
: $this->uri->withQuery($query.'&'.$this->uri->getQuery());
}
if ($this->fields !== []) {
if ($this->body !== null) {
throw new LogicException('Retrofit: Cannot mix @Field and @Body annotations.');
}
$this->body = Psr7\stream_for(implode('&', $this->fields));
}
if ($this->parts !== []) {
if ($this->body !== null) {
throw new LogicException('Retrofit: Cannot mix @Part and @Body annotations.');
}
$this->body = new MultipartStream($this->parts);
}
return new Request($this->method, $uri, $this->headers, $this->body);
}
}
================================================
FILE: src/Internal/RetrofitResponse.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal;
use Psr\Http\Message\ResponseInterface;
use Tebru\Retrofit\Response;
/**
* Wraps a PSR-7 [@see ResponseInterface] and provides convenience methods for getting
* a converted success or error body.
*
* @author Nate Brunette <n@tebru.net>
*/
final class RetrofitResponse implements Response
{
/**
* The PSR-7 response
*
* @var ResponseInterface
*/
private $response;
/**
* Converted body on success
*
* @var mixed
*/
private $body;
/**
* Converted body on failure
*
* @var mixed
*/
private $errorBody;
/**
* Constructor
*
* @param ResponseInterface $response
* @param mixed $body
* @param mixed $errorBody
*/
public function __construct(ResponseInterface $response, $body, $errorBody)
{
$this->response = $response;
$this->body = $body;
$this->errorBody = $errorBody;
}
/**
* Get the raw PSR-7 response
*
* @return ResponseInterface
*/
public function raw(): ResponseInterface
{
return $this->response;
}
/**
* Get the response status code
*
* @return int
*/
public function code(): int
{
return $this->response->getStatusCode();
}
/**
* Get the response message
*
* @return string
*/
public function message(): string
{
return $this->response->getReasonPhrase();
}
/**
* Get response headers
*
* @return array
*/
public function headers(): array
{
return $this->response->getHeaders();
}
/**
* Returns true if the response was successful
*
* @return bool
*/
public function isSuccessful(): bool
{
return $this->response->getStatusCode() >= 200 && $this->response->getStatusCode() < 300;
}
/**
* Get converted body
*
* @return mixed
*/
public function body()
{
return $this->body;
}
/**
* Get converted body on errors
*
* @return mixed
*/
public function errorBody()
{
return $this->errorBody;
}
}
================================================
FILE: src/Internal/ServiceMethod/DefaultServiceMethod.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ServiceMethod;
use LogicException;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Tebru\Retrofit\Call;
use Tebru\Retrofit\CallAdapter;
use Tebru\Retrofit\Internal\RequestBuilder;
use Tebru\Retrofit\ParameterHandler;
use Tebru\Retrofit\ResponseBodyConverter;
use Tebru\Retrofit\Internal\ServiceMethod;
/**
* Class DefaultServiceMethod
*
* This is the class that [@see Call]s will interact with. Its main responsibility is taking
* known request parameters and applying arguments supplied at runtime to build a PSR-7 request
* object. Additionally, it converts responses and adapts [@Call]s.
*
* @author Nate Brunette <n@tebru.net>
*/
final class DefaultServiceMethod implements ServiceMethod
{
/**
* Request method
*
* @var string
*/
private $method;
/**
* Request base url
*
* @var string
*/
private $baseUrl;
/**
* Request path
*
* @var string
*/
private $path;
/**
* @var array
*/
private $headers;
/**
* Array of parameter handlers
*
* @var ParameterHandler[]
*/
private $parameterHandlers;
/**
* The call adapter to use
*
* @var CallAdapter
*/
private $callAdapter;
/**
* Response body converter
*
* @var ResponseBodyConverter
*/
private $responseBodyConverter;
/**
* Error body converter
*
* @var ResponseBodyConverter
*/
private $errorBodyConverter;
/**
* Constructor
*
* @param string $method
* @param string $baseUrl
* @param string $uri
* @param array $headers
* @param array $parameterHandlers
* @param CallAdapter $callAdapter
* @param ResponseBodyConverter $responseBodyConverter
* @param ResponseBodyConverter $errorBodyConverter
*/
public function __construct(
string $method,
string $baseUrl,
string $uri,
array $headers,
array $parameterHandlers,
CallAdapter $callAdapter,
ResponseBodyConverter $responseBodyConverter,
ResponseBodyConverter $errorBodyConverter
) {
$this->method = $method;
$this->baseUrl = $baseUrl;
$this->path = $uri;
$this->headers = $headers;
$this->parameterHandlers = $parameterHandlers;
$this->callAdapter = $callAdapter;
$this->responseBodyConverter = $responseBodyConverter;
$this->errorBodyConverter = $errorBodyConverter;
}
/**
* Apply runtime arguments and build request
*
* @param array $args
* @return RequestInterface
* @throws \LogicException
*/
public function toRequest(array $args): RequestInterface
{
if (\count($this->parameterHandlers) !== \count($args)) {
throw new LogicException(sprintf(
'Retrofit: Incompatible number of arguments. Expected %d and got %s. This either ' .
'means that the service method was not called with the correct number of parameters, ' .
'or there is not an annotation for every parameter.',
\count($this->parameterHandlers),
\count($args)
));
}
$requestBuilder = new RequestBuilder(
$this->method,
$this->baseUrl,
$this->path,
$this->headers
);
foreach ($this->parameterHandlers as $index => $parameterHandler) {
$parameterHandler->apply($requestBuilder, $args[$index]);
}
return $requestBuilder->build();
}
/**
* Take a response and convert it to expected value
*
* @param ResponseInterface $response
* @return mixed
*/
public function toResponseBody(ResponseInterface $response)
{
return $this->responseBodyConverter->convert($response->getBody());
}
/**
* Take a response and convert it to expected value
*
* @param ResponseInterface $response
* @return mixed
*/
public function toErrorBody(ResponseInterface $response)
{
return $this->errorBodyConverter->convert($response->getBody());
}
/**
* Take a [@see Call] and adapt to expected value
*
* @param Call $call
* @return mixed
*/
public function adapt(Call $call)
{
return $this->callAdapter->adapt($call);
}
}
================================================
FILE: src/Internal/ServiceMethod/DefaultServiceMethodBuilder.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ServiceMethod;
use LogicException;
use Tebru\Retrofit\CallAdapter;
use Tebru\Retrofit\ParameterHandler;
use Tebru\Retrofit\ResponseBodyConverter;
use Tebru\Retrofit\ServiceMethodBuilder;
/**
* Class DefaultServiceMethodBuilder
*
* Constructs a [@ServiceMethod]
*
* @author Nate Brunette <n@tebru.net>
*/
final class DefaultServiceMethodBuilder implements ServiceMethodBuilder
{
/**
* The request method
*
* @var string
*/
private $method;
/**
* The request base url
*
* @var string
*/
private $baseUrl;
/**
* The request path
*
* @var string
*/
private $path;
/**
* True if the request has a body
*
* @var bool
*/
private $hasBody;
/**
* The request body content type
*
* @var string
*/
private $contentType;
/**
* Array of headers
*
* @var array
*/
private $headers = [];
/**
* Array of Parameter handlers, indexed to match the position of the parameters
*
* @var ParameterHandler[]
*/
private $parameterHandlers = [];
/**
* Converts successful response bodies to expected value
*
* @var ResponseBodyConverter
*/
private $responseBodyConverter;
/**
* Converts error response bodies to expected value
*
* @var ResponseBodyConverter
*/
private $errorBodyConverter;
/**
* Adapts a [@see Call] to expected value
*
* @var CallAdapter
*/
private $callAdapter;
/**
* Set the request method (e.g. GET, POST)
*
* @param string $method
* @return ServiceMethodBuilder
* @throws \LogicException
*/
public function setMethod(string $method): ServiceMethodBuilder
{
if ($this->method !== null) {
throw new LogicException(sprintf(
'Retrofit: Only one http method is allowed. Trying to set %s, but %s already exists',
strtoupper($method),
$this->method
));
}
$this->method = strtoupper($method);
return $this;
}
/**
* Set the request base url (e.g. http://example.com)
*
* @param string $baseUrl
* @return ServiceMethodBuilder
*/
public function setBaseUrl(string $baseUrl): ServiceMethodBuilder
{
$this->baseUrl = $baseUrl;
return $this;
}
/**
* Set the request path
*
* @param string $path
* @return ServiceMethodBuilder
*/
public function setPath(string $path): ServiceMethodBuilder
{
$this->path = $path;
return $this;
}
/**
* Set to true if an annotation exists that denotes a request body. This should also set
* the request content type.
*
* @param bool $hasBody
* @return ServiceMethodBuilder
* @throws \LogicException
*/
public function setHasBody(bool $hasBody): ServiceMethodBuilder
{
if ($this->hasBody !== null && $this->hasBody !== $hasBody) {
throw new LogicException(
'Retrofit: Body cannot be changed after it has been set. This indicates a conflict between ' .
'HTTP Request annotations, body annotations, and request type annotations. For example, ' .
'@GET cannot be used with @Body, @Field, or @Part annotations'
);
}
$this->hasBody = $hasBody;
return $this;
}
/**
* Set the content type of the request. A content type should not be set if there
* isn't a request body.
*
* @param string $contentType
* @return ServiceMethodBuilder
* @throws \LogicException
*/
public function setContentType(string $contentType): ServiceMethodBuilder
{
if ($this->contentType !== null && $this->contentType !== $contentType) {
throw new LogicException(
'Retrofit: Content type cannot be changed after it has been set. This indicates a conflict between ' .
'HTTP Request annotations, body annotations, and request type annotations. For example, ' .
'@GET cannot be used with @Body, @Field, or @Part annotations'
);
}
$this->contentType = $contentType;
return $this;
}
/**
* Convenience method to declare that the request has content and is json
*
* @return ServiceMethodBuilder
* @throws \LogicException
*/
public function setIsJson(): ServiceMethodBuilder
{
$this->setHasBody(true);
$this->setContentType('application/json');
return $this;
}
/**
* Convenience method to declare that the request has content and is form encoded
*
* @return ServiceMethodBuilder
* @throws \LogicException
*/
public function setIsFormUrlEncoded(): ServiceMethodBuilder
{
$this->setHasBody(true);
$this->setContentType('application/x-www-form-urlencoded');
return $this;
}
/**
* Convenience method to declare that the request has content and is multipart
*
* @return ServiceMethodBuilder
* @throws \LogicException
*/
public function setIsMultipart(): ServiceMethodBuilder
{
$this->setHasBody(true);
$this->setContentType('multipart/form-data');
return $this;
}
/**
* Add a request header. Header name should be normalized.
*
* @param string $name
* @param string $header
* @return ServiceMethodBuilder
*/
public function addHeader(string $name, string $header): ServiceMethodBuilder
{
$this->headers[strtolower($name)][] = $header;
return $this;
}
/**
* Add a [@see ParameterHandler] at the position the parameter exists
*
* @param int $index
* @param ParameterHandler $parameterHandler
* @return ServiceMethodBuilder
*/
public function addParameterHandler(int $index, ParameterHandler $parameterHandler): ServiceMethodBuilder
{
$this->parameterHandlers[$index] = $parameterHandler;
return $this;
}
/**
* Set the [@see CallAdapter]
*
* @param CallAdapter $callAdapter
* @return ServiceMethodBuilder
*/
public function setCallAdapter(CallAdapter $callAdapter): ServiceMethodBuilder
{
$this->callAdapter = $callAdapter;
return $this;
}
/**
* Set the response body converter to convert successful responses
*
* @param ResponseBodyConverter $responseBodyConverter
* @return ServiceMethodBuilder
*/
public function setResponseBodyConverter(ResponseBodyConverter $responseBodyConverter): ServiceMethodBuilder
{
$this->responseBodyConverter = $responseBodyConverter;
return $this;
}
/**
* Set the response body converter to convert error responses
*
* @param ResponseBodyConverter $errorBodyConverter
* @return ServiceMethodBuilder
*/
public function setErrorBodyConverter(ResponseBodyConverter $errorBodyConverter): ServiceMethodBuilder
{
$this->errorBodyConverter = $errorBodyConverter;
return $this;
}
/**
* Create a new [@see DefaultServiceMethod] from previously set parameters
*
* @return DefaultServiceMethod
* @throws \LogicException
*/
public function build(): DefaultServiceMethod
{
if ($this->method === null) {
throw new LogicException(
'Retrofit: Cannot build service method without HTTP method. Please specify @GET, @POST, etc'
);
}
if ($this->baseUrl === null) {
throw new LogicException(
'Retrofit: Cannot build service method without base url. Please specify on RetrofitBuilder'
);
}
if ($this->path === null) {
throw new LogicException(
'Retrofit: Cannot build service method without HTTP method. Please specify @GET, @POST, etc'
);
}
if ($this->hasBody === true && $this->contentType === null) {
throw new LogicException(
'Retrofit: Cannot build service method with body and no content type. Set one using @Body, ' .
'@Field, or @Part'
);
}
if ($this->hasBody !== true && $this->contentType !== null) {
throw new LogicException(
'Retrofit: Cannot set a content-type without a body. This indicates a conflict between ' .
'HTTP Request annotations, body annotations, and request type annotations. For example, ' .
'@GET cannot be used with @Body, @Field, or @Part annotations'
);
}
if ($this->responseBodyConverter === null) {
throw new LogicException(
'Retrofit: Cannot build service method without response body converter'
);
}
if ($this->errorBodyConverter === null) {
throw new LogicException(
'Retrofit: Cannot build service method without error body converter'
);
}
if ($this->callAdapter === null) {
throw new LogicException(
'Retrofit: Cannot build service method without call adapter'
);
}
if ($this->contentType !== null && !isset($this->headers['content-type'])) {
$this->addHeader('content-type', $this->contentType);
}
if ($this->hasBody === null) {
$this->hasBody = false;
}
ksort($this->parameterHandlers);
return new DefaultServiceMethod(
$this->method,
$this->baseUrl,
$this->path,
$this->headers,
$this->parameterHandlers,
$this->callAdapter,
$this->responseBodyConverter,
$this->errorBodyConverter
);
}
}
================================================
FILE: src/Internal/ServiceMethod/ServiceMethodFactory.php
================================================
<?php
/*
* Copyright (c) Nate Brunette.
* Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
declare(strict_types=1);
namespace Tebru\Retrofit\Internal\ServiceMethod;
use LogicException;
use Psr\Http\Message\StreamInterface;
use ReflectionMethod;
use Tebru\AnnotationReader\AbstractAnnotation;
use Tebru\AnnotationReader\AnnotationCollection;
use Tebru\AnnotationReader\AnnotationReaderAdapter;
use Tebru\PhpType\TypeToken;
use Tebru\Retrofit\Annotation as Annot;
use Tebru\Retrofit\CallAdapter;
use Tebru\Retrofit\Converter;
use Tebru\Retrofit\Internal\AnnotationProcessor;
use Tebru\Retrofit\Internal\CallAdapter\CallAdapterProvider;
use Tebru\Retrofit\Internal\Converter\ConverterProvider;
use Tebru\Retrofit\ServiceMethodBuilder;
/**
* Class ServiceMethodFactory
*
* Creates and sets up values for [@see ServiceMethodBuilder], then delegates final setup
* to annotation handles.
*
* @author Nate Brunette <n@tebru.net>
*/
final class ServiceMethodFactory
{
/**
* Handles an [@see AbstractAnnotation]
*
* @var AnnotationProcessor
*/
private $annotationProcessor;
/**
* Fetches a [@see CallAdapter]
*
* @var CallAdapterProvider
*/
private $callAdapterProvider;
/**
* Fetches a [@see Converter]
*
* @var ConverterProvider
*/
private $converterProvider;
/**
* Reads annotations from service interface
*
* @var AnnotationReaderAdapter
*/
private $annotationReader;
/**
* The request base url
*
* @var string
*/
private $baseUrl;
/**
* Constructor
*
* @param AnnotationProcessor $annotationProcessor
* @param CallAdapterProvider $callAdapterProvider
* @param ConverterProvider $converterProvider
* @param AnnotationReaderAdapter $annotationReader
* @param string $baseUrl
*/
public function __construct(
AnnotationProcessor $annotationProcessor,
CallAdapterProvider $callAdapterProvider,
ConverterProvider $converterProvider,
AnnotationReaderAdapter $annotationReader,
string $baseUrl
) {
$this->annotationProcessor = $annotationProcessor;
$this->callAdapterProvider = $callAdapterProvider;
$this->converterProvider = $converterProvider;
$this->annotationReader = $annotationReader;
$this->baseUrl = $baseUrl;
}
/**
* Creates a [@see DefaultServiceMethod]
*
* @param string $interfaceName
* @param string $methodName
* @return DefaultServiceMethod
* @throws \LogicException
*/
public function create(string $interfaceName, string $methodName): DefaultServiceMethod
{
$serviceMethodBuilder = new DefaultServiceMethodBuilder();
$annotations = $this->annotationReader->readMethod($methodName, $interfaceName, true, true);
$reflectionMethod = new ReflectionMethod($interfaceName, $methodName);
$returnType = $reflectionMethod->getReturnType();
if ($returnType === null) {
throw new LogicException(sprintf(
'Retrofit: All service methods must contain a return type. None found for %s::%s()',
$reflectionMethod->getDeclaringClass()->name,
$reflectionMeth
gitextract_syq3xf3v/
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── CONTRIBUTORS.md
├── LICENSE.md
├── README.md
├── bin/
│ └── retrofit
├── composer.json
├── docs/
│ ├── advanced_usage.md
│ ├── annotations.md
│ ├── installation.md
│ ├── upgrade_2_3.md
│ └── usage.md
├── phpcs.xml
├── phpunit.xml
├── src/
│ ├── Annotation/
│ │ ├── Body.php
│ │ ├── DELETE.php
│ │ ├── Encodable.php
│ │ ├── ErrorBody.php
│ │ ├── Field.php
│ │ ├── FieldMap.php
│ │ ├── GET.php
│ │ ├── HEAD.php
│ │ ├── Header.php
│ │ ├── HeaderMap.php
│ │ ├── Headers.php
│ │ ├── HttpRequest.php
│ │ ├── OPTIONS.php
│ │ ├── PATCH.php
│ │ ├── POST.php
│ │ ├── PUT.php
│ │ ├── ParameterAnnotation.php
│ │ ├── ParameterAwareAnnotation.php
│ │ ├── Part.php
│ │ ├── PartMap.php
│ │ ├── Path.php
│ │ ├── Query.php
│ │ ├── QueryMap.php
│ │ ├── QueryName.php
│ │ ├── REQUEST.php
│ │ ├── ResponseBody.php
│ │ └── Url.php
│ ├── AnnotationHandler.php
│ ├── Call.php
│ ├── CallAdapter.php
│ ├── CallAdapterFactory.php
│ ├── Command/
│ │ └── CompileCommand.php
│ ├── Converter.php
│ ├── ConverterFactory.php
│ ├── DefaultProxyFactoryAware.php
│ ├── Exception/
│ │ └── ResponseHandlingFailedException.php
│ ├── Finder/
│ │ └── ServiceResolver.php
│ ├── Http/
│ │ └── MultipartBody.php
│ ├── HttpClient.php
│ ├── Internal/
│ │ ├── AnnotationHandler/
│ │ │ ├── BodyAnnotHandler.php
│ │ │ ├── FieldAnnotHandler.php
│ │ │ ├── FieldMapAnnotHandler.php
│ │ │ ├── HeaderAnnotHandler.php
│ │ │ ├── HeaderMapAnnotHandler.php
│ │ │ ├── HeadersAnnotHandler.php
│ │ │ ├── HttpRequestAnnotHandler.php
│ │ │ ├── PartAnnotHandler.php
│ │ │ ├── PartMapAnnotHandler.php
│ │ │ ├── PathAnnotHandler.php
│ │ │ ├── QueryAnnotHandler.php
│ │ │ ├── QueryMapAnnotHandler.php
│ │ │ ├── QueryNameAnnotHandler.php
│ │ │ └── UrlAnnotHandler.php
│ │ ├── AnnotationProcessor.php
│ │ ├── CacheProvider.php
│ │ ├── CallAdapter/
│ │ │ ├── CallAdapterProvider.php
│ │ │ ├── DefaultCallAdapter.php
│ │ │ └── DefaultCallAdapterFactory.php
│ │ ├── Converter/
│ │ │ ├── ConverterProvider.php
│ │ │ ├── DefaultConverterFactory.php
│ │ │ ├── DefaultRequestBodyConverter.php
│ │ │ ├── DefaultResponseBodyConverter.php
│ │ │ ├── DefaultStringConverter.php
│ │ │ └── NoopStringConverter.php
│ │ ├── DefaultProxyFactory.php
│ │ ├── Filesystem.php
│ │ ├── HttpClientCall.php
│ │ ├── ParameterHandler/
│ │ │ ├── AbstractParameterHandler.php
│ │ │ ├── BodyParamHandler.php
│ │ │ ├── FieldMapParamHandler.php
│ │ │ ├── FieldParamHandler.php
│ │ │ ├── HeaderMapParamHandler.php
│ │ │ ├── HeaderParamHandler.php
│ │ │ ├── PartMapParamHandler.php
│ │ │ ├── PartParamHandler.php
│ │ │ ├── PathParamHandler.php
│ │ │ ├── QueryMapParamHandler.php
│ │ │ ├── QueryNameParamHandler.php
│ │ │ ├── QueryParamHandler.php
│ │ │ └── UrlParamHandler.php
│ │ ├── RequestBuilder.php
│ │ ├── RetrofitResponse.php
│ │ ├── ServiceMethod/
│ │ │ ├── DefaultServiceMethod.php
│ │ │ ├── DefaultServiceMethodBuilder.php
│ │ │ └── ServiceMethodFactory.php
│ │ └── ServiceMethod.php
│ ├── ParameterHandler.php
│ ├── Proxy/
│ │ └── AbstractProxy.php
│ ├── Proxy.php
│ ├── ProxyFactory.php
│ ├── RequestBodyConverter.php
│ ├── Response.php
│ ├── ResponseBodyConverter.php
│ ├── Retrofit.php
│ ├── RetrofitBuilder.php
│ ├── ServiceMethodBuilder.php
│ └── StringConverter.php
└── tests/
├── Mock/
│ └── Unit/
│ ├── Internal/
│ │ ├── AnnotationProcessorTest/
│ │ │ ├── AnnotationProcessorTestMock.php
│ │ │ └── BadConverterAnnotation.php
│ │ ├── HttpClientCallTest/
│ │ │ ├── HttpClientCallTestClientMock.php
│ │ │ ├── HttpClientCallTestErrorBodyMock.php
│ │ │ ├── HttpClientCallTestResponseBodyMock.php
│ │ │ └── HttpClientCallTestServiceMethodMock.php
│ │ ├── ProxyFactoryTest/
│ │ │ ├── PFTCTestCreate.php
│ │ │ ├── PFTCTestCreateCacheDirectoryFail.php
│ │ │ ├── PFTCTestCreateClientFail.php
│ │ │ ├── PFTCTestCreateTwice.php
│ │ │ ├── PFTCTestCreateWithoutCache.php
│ │ │ ├── ProxyFactoryTestClientNoReturnType.php
│ │ │ ├── ProxyFactoryTestClientNoTypehint.php
│ │ │ ├── ProxyFactoryTestFilesystem.php
│ │ │ └── ProxyFactoryTestHttpClient.php
│ │ └── ServiceMethod/
│ │ └── ServiceMethodFactoryTest/
│ │ ├── ServiceMethodFactoryTestClient.php
│ │ └── ServiceMethodFactoryTestConverterFactory.php
│ ├── MockCall.php
│ ├── MockConverterFactory.php
│ └── RetrofitTest/
│ ├── ApiClient.php
│ ├── CacheableApiClient.php
│ ├── DefaultParamsApiClient.php
│ ├── InvalidSyntaxApiClient.php
│ ├── RetrofitTestAdaptedCallMock.php
│ ├── RetrofitTestCallAdapterFactory.php
│ ├── RetrofitTestCallAdapterMock.php
│ ├── RetrofitTestConverterFactory.php
│ ├── RetrofitTestCustomAnnotation.php
│ ├── RetrofitTestCustomAnnotationHandler.php
│ ├── RetrofitTestDelegateProxy.php
│ ├── RetrofitTestHttpClient.php
│ ├── RetrofitTestProxyFactory.php
│ ├── RetrofitTestRequestBodyConverter.php
│ ├── RetrofitTestRequestBodyMock.php
│ ├── RetrofitTestResponseBodyConverter.php
│ └── RetrofitTestResponseBodyMock.php
├── Unit/
│ ├── Internal/
│ │ ├── AnnotationHandlersTest.php
│ │ ├── AnnotationProcessorTest.php
│ │ ├── CallAdapterTest.php
│ │ ├── ConverterTest.php
│ │ ├── DefaultProxyFactoryTest.php
│ │ ├── FilesystemTest.php
│ │ ├── HttpClientCallTest.php
│ │ ├── ParameterHandlersTest.php
│ │ ├── RequestBuilderTest.php
│ │ ├── RetrofitResponseTest.php
│ │ └── ServiceMethod/
│ │ ├── DefaultServiceMethodBuilderTest.php
│ │ ├── ServiceMethodFactoryTest.php
│ │ └── ServiceMethodTest.php
│ └── RetrofitTest.php
└── bootstrap.php
SYMBOL INDEX (663 symbols across 147 files)
FILE: src/Annotation/Body.php
class Body (line 24) | class Body extends ParameterAnnotation
method converterType (line 33) | public function converterType(): ?string
FILE: src/Annotation/DELETE.php
class DELETE (line 21) | class DELETE extends AbstractAnnotation implements HttpRequest
method getType (line 28) | public function getType(): string
method hasBody (line 38) | public function hasBody(): bool
FILE: src/Annotation/Encodable.php
class Encodable (line 19) | abstract class Encodable extends ParameterAnnotation
method init (line 31) | protected function init(): void
method isEncoded (line 43) | public function isEncoded(): bool
method converterType (line 55) | public function converterType(): ?string
FILE: src/Annotation/ErrorBody.php
class ErrorBody (line 21) | class ErrorBody extends AbstractAnnotation
FILE: src/Annotation/Field.php
class Field (line 24) | class Field extends Encodable
method allowMultiple (line 32) | public function allowMultiple(): bool
FILE: src/Annotation/FieldMap.php
class FieldMap (line 24) | class FieldMap extends Encodable
method allowMultiple (line 31) | public function allowMultiple(): bool
FILE: src/Annotation/GET.php
class GET (line 21) | class GET extends AbstractAnnotation implements HttpRequest
method getType (line 28) | public function getType(): string
method hasBody (line 38) | public function hasBody(): bool
FILE: src/Annotation/HEAD.php
class HEAD (line 21) | class HEAD extends AbstractAnnotation implements HttpRequest
method getType (line 28) | public function getType(): string
method hasBody (line 38) | public function hasBody(): bool
FILE: src/Annotation/Header.php
class Header (line 24) | class Header extends ParameterAnnotation
method allowMultiple (line 32) | public function allowMultiple(): bool
method converterType (line 44) | public function converterType(): ?string
FILE: src/Annotation/HeaderMap.php
class HeaderMap (line 22) | class HeaderMap extends ParameterAnnotation
method converterType (line 31) | public function converterType(): ?string
method allowMultiple (line 41) | public function allowMultiple(): bool
FILE: src/Annotation/Headers.php
class Headers (line 29) | class Headers extends AbstractAnnotation
method init (line 36) | protected function init(): void
FILE: src/Annotation/HttpRequest.php
type HttpRequest (line 18) | interface HttpRequest
method getType (line 25) | public function getType(): string;
method hasBody (line 32) | public function hasBody(): bool;
method getValue (line 39) | public function getValue();
FILE: src/Annotation/OPTIONS.php
class OPTIONS (line 21) | class OPTIONS extends AbstractAnnotation implements HttpRequest
method getType (line 28) | public function getType(): string
method hasBody (line 38) | public function hasBody(): bool
FILE: src/Annotation/PATCH.php
class PATCH (line 21) | class PATCH extends AbstractAnnotation implements HttpRequest
method getType (line 28) | public function getType(): string
method hasBody (line 38) | public function hasBody(): bool
FILE: src/Annotation/POST.php
class POST (line 21) | class POST extends AbstractAnnotation implements HttpRequest
method getType (line 28) | public function getType(): string
method hasBody (line 38) | public function hasBody(): bool
FILE: src/Annotation/PUT.php
class PUT (line 21) | class PUT extends AbstractAnnotation implements HttpRequest
method getType (line 28) | public function getType(): string
method hasBody (line 38) | public function hasBody(): bool
FILE: src/Annotation/ParameterAnnotation.php
class ParameterAnnotation (line 19) | abstract class ParameterAnnotation extends AbstractAnnotation implements...
method init (line 31) | protected function init(): void
method getVariableName (line 43) | public function getVariableName(): string
FILE: src/Annotation/ParameterAwareAnnotation.php
type ParameterAwareAnnotation (line 19) | interface ParameterAwareAnnotation
method getVariableName (line 27) | public function getVariableName(): string;
method converterType (line 36) | public function converterType(): ?string;
FILE: src/Annotation/Part.php
class Part (line 27) | class Part extends ParameterAnnotation
method init (line 39) | protected function init(): void
method getEncoding (line 51) | public function getEncoding(): string
method allowMultiple (line 62) | public function allowMultiple(): bool
method converterType (line 74) | public function converterType(): ?string
FILE: src/Annotation/PartMap.php
class PartMap (line 30) | class PartMap extends ParameterAnnotation
method init (line 42) | protected function init(): void
method getEncoding (line 54) | public function getEncoding(): string
method converterType (line 66) | public function converterType(): ?string
method allowMultiple (line 76) | public function allowMultiple(): bool
FILE: src/Annotation/Path.php
class Path (line 32) | class Path extends ParameterAnnotation
method allowMultiple (line 39) | public function allowMultiple(): bool
method converterType (line 51) | public function converterType(): ?string
FILE: src/Annotation/Query.php
class Query (line 24) | class Query extends Encodable
method allowMultiple (line 32) | public function allowMultiple(): bool
FILE: src/Annotation/QueryMap.php
class QueryMap (line 24) | class QueryMap extends Encodable
method allowMultiple (line 31) | public function allowMultiple(): bool
FILE: src/Annotation/QueryName.php
class QueryName (line 21) | class QueryName extends Encodable
method allowMultiple (line 29) | public function allowMultiple(): bool
FILE: src/Annotation/REQUEST.php
class REQUEST (line 24) | class REQUEST extends AbstractAnnotation implements HttpRequest
method init (line 43) | protected function init(): void
method getType (line 57) | public function getType(): string
method hasBody (line 67) | public function hasBody(): bool
FILE: src/Annotation/ResponseBody.php
class ResponseBody (line 21) | class ResponseBody extends AbstractAnnotation
FILE: src/Annotation/Url.php
class Url (line 21) | class Url extends ParameterAnnotation
method converterType (line 30) | public function converterType(): ?string
FILE: src/AnnotationHandler.php
type AnnotationHandler (line 21) | interface AnnotationHandler
method handle (line 32) | public function handle(
FILE: src/Call.php
type Call (line 21) | interface Call
method execute (line 30) | public function execute(): Response;
method enqueue (line 55) | public function enqueue(?callable $onResponse = null, ?callable $onFai...
method wait (line 62) | public function wait(): void;
method request (line 69) | public function request(): RequestInterface;
FILE: src/CallAdapter.php
type CallAdapter (line 19) | interface CallAdapter
method adapt (line 27) | public function adapt(Call $call);
FILE: src/CallAdapterFactory.php
type CallAdapterFactory (line 18) | interface CallAdapterFactory
method supports (line 26) | public function supports(TypeToken $type): bool;
method create (line 34) | public function create(TypeToken $type): CallAdapter;
FILE: src/Command/CompileCommand.php
class CompileCommand (line 28) | class CompileCommand extends Command
method configure (line 35) | protected function configure(): void
method execute (line 50) | protected function execute(InputInterface $input, OutputInterface $out...
FILE: src/Converter.php
type Converter (line 18) | interface Converter
FILE: src/ConverterFactory.php
type ConverterFactory (line 24) | interface ConverterFactory
method responseBodyConverter (line 32) | public function responseBodyConverter(TypeToken $type): ?ResponseBodyC...
method requestBodyConverter (line 40) | public function requestBodyConverter(TypeToken $type): ?RequestBodyCon...
method stringConverter (line 48) | public function stringConverter(TypeToken $type): ?StringConverter;
FILE: src/DefaultProxyFactoryAware.php
type DefaultProxyFactoryAware (line 19) | interface DefaultProxyFactoryAware
method setDefaultProxyFactory (line 27) | public function setDefaultProxyFactory(ProxyFactory $proxyFactory): void;
FILE: src/Exception/ResponseHandlingFailedException.php
class ResponseHandlingFailedException (line 26) | class ResponseHandlingFailedException extends RuntimeException
method __construct (line 46) | public function __construct(
method getRequest (line 61) | public function getRequest(): RequestInterface
method getResponse (line 69) | public function getResponse(): ResponseInterface
FILE: src/Finder/ServiceResolver.php
class ServiceResolver (line 21) | class ServiceResolver
method findServices (line 34) | public function findServices(string $srcDir): array
FILE: src/Http/MultipartBody.php
class MultipartBody (line 20) | final class MultipartBody
method __construct (line 50) | public function __construct(string $name, $contents, array $headers = ...
method getName (line 61) | public function getName(): string
method getContents (line 69) | public function getContents(): StreamInterface
method getHeaders (line 77) | public function getHeaders(): array
method getFilename (line 85) | public function getFilename(): ?string
FILE: src/HttpClient.php
type HttpClient (line 21) | interface HttpClient
method send (line 29) | public function send(RequestInterface $request): ResponseInterface;
method sendAsync (line 45) | public function sendAsync(RequestInterface $request, callable $onRespo...
method wait (line 52) | public function wait(): void;
FILE: src/Internal/AnnotationHandler/BodyAnnotHandler.php
class BodyAnnotHandler (line 24) | final class BodyAnnotHandler implements AnnotationHandler
method handle (line 36) | public function handle(
FILE: src/Internal/AnnotationHandler/FieldAnnotHandler.php
class FieldAnnotHandler (line 26) | final class FieldAnnotHandler implements AnnotationHandler
method handle (line 38) | public function handle(
FILE: src/Internal/AnnotationHandler/FieldMapAnnotHandler.php
class FieldMapAnnotHandler (line 26) | final class FieldMapAnnotHandler implements AnnotationHandler
method handle (line 38) | public function handle(
FILE: src/Internal/AnnotationHandler/HeaderAnnotHandler.php
class HeaderAnnotHandler (line 24) | final class HeaderAnnotHandler implements AnnotationHandler
method handle (line 36) | public function handle(
FILE: src/Internal/AnnotationHandler/HeaderMapAnnotHandler.php
class HeaderMapAnnotHandler (line 24) | final class HeaderMapAnnotHandler implements AnnotationHandler
method handle (line 36) | public function handle(
FILE: src/Internal/AnnotationHandler/HeadersAnnotHandler.php
class HeadersAnnotHandler (line 22) | final class HeadersAnnotHandler implements AnnotationHandler
method handle (line 34) | public function handle(
FILE: src/Internal/AnnotationHandler/HttpRequestAnnotHandler.php
class HttpRequestAnnotHandler (line 23) | final class HttpRequestAnnotHandler implements AnnotationHandler
method handle (line 36) | public function handle(
FILE: src/Internal/AnnotationHandler/PartAnnotHandler.php
class PartAnnotHandler (line 25) | final class PartAnnotHandler implements AnnotationHandler
method handle (line 37) | public function handle(
FILE: src/Internal/AnnotationHandler/PartMapAnnotHandler.php
class PartMapAnnotHandler (line 25) | final class PartMapAnnotHandler implements AnnotationHandler
method handle (line 37) | public function handle(
FILE: src/Internal/AnnotationHandler/PathAnnotHandler.php
class PathAnnotHandler (line 24) | final class PathAnnotHandler implements AnnotationHandler
method handle (line 36) | public function handle(
FILE: src/Internal/AnnotationHandler/QueryAnnotHandler.php
class QueryAnnotHandler (line 26) | final class QueryAnnotHandler implements AnnotationHandler
method handle (line 38) | public function handle(
FILE: src/Internal/AnnotationHandler/QueryMapAnnotHandler.php
class QueryMapAnnotHandler (line 26) | final class QueryMapAnnotHandler implements AnnotationHandler
method handle (line 38) | public function handle(
FILE: src/Internal/AnnotationHandler/QueryNameAnnotHandler.php
class QueryNameAnnotHandler (line 26) | final class QueryNameAnnotHandler implements AnnotationHandler
method handle (line 38) | public function handle(
FILE: src/Internal/AnnotationHandler/UrlAnnotHandler.php
class UrlAnnotHandler (line 24) | final class UrlAnnotHandler implements AnnotationHandler
method handle (line 36) | public function handle(
FILE: src/Internal/AnnotationProcessor.php
class AnnotationProcessor (line 30) | final class AnnotationProcessor
method __construct (line 44) | public function __construct(array $handlers)
method process (line 58) | public function process(
method findMethodParameterIndex (line 91) | private function findMethodParameterIndex(ReflectionMethod $reflection...
method getParameterType (line 117) | private function getParameterType(ReflectionMethod $reflectionMethod, ...
method getConverter (line 143) | private function getConverter(
FILE: src/Internal/CacheProvider.php
class CacheProvider (line 23) | final class CacheProvider
method createFileCache (line 32) | public static function createFileCache(string $cacheDir): CacheInterface
method createMemoryCache (line 52) | public static function createMemoryCache(): CacheInterface
method createNullCache (line 67) | public static function createNullCache(): CacheInterface
FILE: src/Internal/CallAdapter/CallAdapterProvider.php
class CallAdapterProvider (line 23) | final class CallAdapterProvider
method __construct (line 35) | public function __construct(array $callAdapterFactories)
method get (line 47) | public function get(TypeToken $type): CallAdapter
FILE: src/Internal/CallAdapter/DefaultCallAdapter.php
class DefaultCallAdapter (line 21) | final class DefaultCallAdapter implements CallAdapter
method adapt (line 29) | public function adapt(Call $call): Call
FILE: src/Internal/CallAdapter/DefaultCallAdapterFactory.php
class DefaultCallAdapterFactory (line 23) | final class DefaultCallAdapterFactory implements CallAdapterFactory
method supports (line 31) | public function supports(TypeToken $type): bool
method create (line 42) | public function create(TypeToken $type): CallAdapter
FILE: src/Internal/Converter/ConverterProvider.php
class ConverterProvider (line 25) | final class ConverterProvider
method __construct (line 60) | public function __construct(array $factories)
method getResponseBodyConverter (line 72) | public function getResponseBodyConverter(TypeToken $type): ResponseBod...
method getRequestBodyConverter (line 103) | public function getRequestBodyConverter(TypeToken $type): RequestBodyC...
method getStringConverter (line 134) | public function getStringConverter(TypeToken $type): StringConverter
FILE: src/Internal/Converter/DefaultConverterFactory.php
class DefaultConverterFactory (line 25) | final class DefaultConverterFactory implements ConverterFactory
method responseBodyConverter (line 33) | public function responseBodyConverter(TypeToken $type): ?ResponseBodyC...
method requestBodyConverter (line 48) | public function requestBodyConverter(TypeToken $type): ?RequestBodyCon...
method stringConverter (line 66) | public function stringConverter(TypeToken $type): ?StringConverter
FILE: src/Internal/Converter/DefaultRequestBodyConverter.php
class DefaultRequestBodyConverter (line 21) | final class DefaultRequestBodyConverter implements RequestBodyConverter
method convert (line 29) | public function convert($value): StreamInterface
FILE: src/Internal/Converter/DefaultResponseBodyConverter.php
class DefaultResponseBodyConverter (line 21) | final class DefaultResponseBodyConverter implements ResponseBodyConverter
method convert (line 29) | public function convert(StreamInterface $value): StreamInterface
FILE: src/Internal/Converter/DefaultStringConverter.php
class DefaultStringConverter (line 18) | final class DefaultStringConverter implements StringConverter
method convert (line 26) | public function convert($value): string
FILE: src/Internal/Converter/NoopStringConverter.php
class NoopStringConverter (line 18) | class NoopStringConverter implements StringConverter
method convert (line 27) | public function convert($value): string
FILE: src/Internal/DefaultProxyFactory.php
class DefaultProxyFactory (line 41) | final class DefaultProxyFactory implements ProxyFactory
method __construct (line 91) | public function __construct(
method create (line 120) | public function create(string $service): ?Proxy
method mapArray (line 265) | private function mapArray(array $array): array
FILE: src/Internal/Filesystem.php
class Filesystem (line 16) | class Filesystem
method makeDirectory (line 26) | public function makeDirectory(string $pathname, int $mode = 0777, bool...
method put (line 38) | public function put(string $filename, string $contents): bool
FILE: src/Internal/HttpClientCall.php
class HttpClientCall (line 24) | final class HttpClientCall implements Call
method __construct (line 61) | public function __construct(HttpClient $client, ServiceMethod $service...
method execute (line 75) | public function execute(): Response
method enqueue (line 106) | public function enqueue(?callable $onResponse = null, ?callable $onFai...
method wait (line 132) | public function wait(): void
method request (line 142) | public function request(): RequestInterface
method createResponse (line 157) | private function createResponse(ResponseInterface $response): Retrofit...
FILE: src/Internal/ParameterHandler/AbstractParameterHandler.php
class AbstractParameterHandler (line 23) | abstract class AbstractParameterHandler implements ParameterHandler
method getListValues (line 37) | protected function getListValues($list): Generator
method handlePart (line 61) | protected function handlePart(
FILE: src/Internal/ParameterHandler/BodyParamHandler.php
class BodyParamHandler (line 19) | final class BodyParamHandler extends AbstractParameterHandler
method __construct (line 31) | public function __construct(RequestBodyConverter $converter)
method apply (line 43) | public function apply(RequestBuilder $requestBuilder, $value): void
FILE: src/Internal/ParameterHandler/FieldMapParamHandler.php
class FieldMapParamHandler (line 20) | final class FieldMapParamHandler extends AbstractParameterHandler
method __construct (line 38) | public function __construct(StringConverter $converter, bool $encoded)
method apply (line 51) | public function apply(RequestBuilder $requestBuilder, $map): void
FILE: src/Internal/ParameterHandler/FieldParamHandler.php
class FieldParamHandler (line 19) | final class FieldParamHandler extends AbstractParameterHandler
method __construct (line 43) | public function __construct(StringConverter $converter, string $name, ...
method apply (line 58) | public function apply(RequestBuilder $requestBuilder, $value): void
FILE: src/Internal/ParameterHandler/HeaderMapParamHandler.php
class HeaderMapParamHandler (line 20) | final class HeaderMapParamHandler extends AbstractParameterHandler
method __construct (line 32) | public function __construct(StringConverter $converter)
method apply (line 45) | public function apply(RequestBuilder $requestBuilder, $map): void
FILE: src/Internal/ParameterHandler/HeaderParamHandler.php
class HeaderParamHandler (line 19) | final class HeaderParamHandler extends AbstractParameterHandler
method __construct (line 37) | public function __construct(StringConverter $converter, string $name)
method apply (line 51) | public function apply(RequestBuilder $requestBuilder, $value): void
FILE: src/Internal/ParameterHandler/PartMapParamHandler.php
class PartMapParamHandler (line 20) | final class PartMapParamHandler extends AbstractParameterHandler
method __construct (line 38) | public function __construct(RequestBodyConverter $converter, string $e...
method apply (line 51) | public function apply(RequestBuilder $requestBuilder, $map): void
FILE: src/Internal/ParameterHandler/PartParamHandler.php
class PartParamHandler (line 19) | final class PartParamHandler extends AbstractParameterHandler
method __construct (line 43) | public function __construct(RequestBodyConverter $converter, string $n...
method apply (line 57) | public function apply(RequestBuilder $requestBuilder, $value): void
FILE: src/Internal/ParameterHandler/PathParamHandler.php
class PathParamHandler (line 20) | final class PathParamHandler extends AbstractParameterHandler
method __construct (line 38) | public function __construct(StringConverter $converter, string $name)
method apply (line 52) | public function apply(RequestBuilder $requestBuilder, $value): void
FILE: src/Internal/ParameterHandler/QueryMapParamHandler.php
class QueryMapParamHandler (line 20) | final class QueryMapParamHandler extends AbstractParameterHandler
method __construct (line 38) | public function __construct(StringConverter $converter, bool $encoded)
method apply (line 52) | public function apply(RequestBuilder $requestBuilder, $map): void
FILE: src/Internal/ParameterHandler/QueryNameParamHandler.php
class QueryNameParamHandler (line 19) | final class QueryNameParamHandler extends AbstractParameterHandler
method __construct (line 37) | public function __construct(StringConverter $converter, bool $encoded)
method apply (line 51) | public function apply(RequestBuilder $requestBuilder, $value): void
FILE: src/Internal/ParameterHandler/QueryParamHandler.php
class QueryParamHandler (line 19) | final class QueryParamHandler extends AbstractParameterHandler
method __construct (line 43) | public function __construct(StringConverter $converter, string $name, ...
method apply (line 58) | public function apply(RequestBuilder $requestBuilder, $value): void
FILE: src/Internal/ParameterHandler/UrlParamHandler.php
class UrlParamHandler (line 20) | final class UrlParamHandler extends AbstractParameterHandler
method __construct (line 32) | public function __construct(StringConverter $converter)
method apply (line 45) | public function apply(RequestBuilder $requestBuilder, $value): void
FILE: src/Internal/RequestBuilder.php
class RequestBuilder (line 23) | final class RequestBuilder
method __construct (line 82) | public function __construct(string $method, string $baseUrl, string $u...
method setBaseUrl (line 94) | public function setBaseUrl(string $value): void
method replacePath (line 109) | public function replacePath(string $name, string $value): void
method addQuery (line 123) | public function addQuery(string $name, string $value, bool $encoded): ...
method addQueryName (line 139) | public function addQueryName(string $value, bool $encoded): void
method addHeader (line 154) | public function addHeader(string $name, string $value): void
method setBody (line 165) | public function setBody(StreamInterface $body): void
method addField (line 177) | public function addField(string $name, string $value, bool $encoded): ...
method addPart (line 195) | public function addPart(string $name, StreamInterface $contents, array...
method build (line 211) | public function build(): Request
FILE: src/Internal/RetrofitResponse.php
class RetrofitResponse (line 20) | final class RetrofitResponse implements Response
method __construct (line 50) | public function __construct(ResponseInterface $response, $body, $error...
method raw (line 62) | public function raw(): ResponseInterface
method code (line 72) | public function code(): int
method message (line 82) | public function message(): string
method headers (line 92) | public function headers(): array
method isSuccessful (line 102) | public function isSuccessful(): bool
method body (line 112) | public function body()
method errorBody (line 122) | public function errorBody()
FILE: src/Internal/ServiceMethod.php
type ServiceMethod (line 22) | interface ServiceMethod
method toRequest (line 30) | public function toRequest(array $args): RequestInterface;
method toResponseBody (line 38) | public function toResponseBody(ResponseInterface $response);
method toErrorBody (line 46) | public function toErrorBody(ResponseInterface $response);
method adapt (line 54) | public function adapt(Call $call);
FILE: src/Internal/ServiceMethod/DefaultServiceMethod.php
class DefaultServiceMethod (line 30) | final class DefaultServiceMethod implements ServiceMethod
method __construct (line 98) | public function __construct(
method toRequest (line 125) | public function toRequest(array $args): RequestInterface
method toResponseBody (line 157) | public function toResponseBody(ResponseInterface $response)
method toErrorBody (line 168) | public function toErrorBody(ResponseInterface $response)
method adapt (line 179) | public function adapt(Call $call)
FILE: src/Internal/ServiceMethod/DefaultServiceMethodBuilder.php
class DefaultServiceMethodBuilder (line 24) | final class DefaultServiceMethodBuilder implements ServiceMethodBuilder
method setMethod (line 103) | public function setMethod(string $method): ServiceMethodBuilder
method setBaseUrl (line 124) | public function setBaseUrl(string $baseUrl): ServiceMethodBuilder
method setPath (line 137) | public function setPath(string $path): ServiceMethodBuilder
method setHasBody (line 152) | public function setHasBody(bool $hasBody): ServiceMethodBuilder
method setContentType (line 175) | public function setContentType(string $contentType): ServiceMethodBuilder
method setIsJson (line 196) | public function setIsJson(): ServiceMethodBuilder
method setIsFormUrlEncoded (line 211) | public function setIsFormUrlEncoded(): ServiceMethodBuilder
method setIsMultipart (line 225) | public function setIsMultipart(): ServiceMethodBuilder
method addHeader (line 240) | public function addHeader(string $name, string $header): ServiceMethod...
method addParameterHandler (line 254) | public function addParameterHandler(int $index, ParameterHandler $para...
method setCallAdapter (line 267) | public function setCallAdapter(CallAdapter $callAdapter): ServiceMetho...
method setResponseBodyConverter (line 280) | public function setResponseBodyConverter(ResponseBodyConverter $respon...
method setErrorBodyConverter (line 293) | public function setErrorBodyConverter(ResponseBodyConverter $errorBody...
method build (line 306) | public function build(): DefaultServiceMethod
FILE: src/Internal/ServiceMethod/ServiceMethodFactory.php
class ServiceMethodFactory (line 34) | final class ServiceMethodFactory
method __construct (line 80) | public function __construct(
method create (line 102) | public function create(string $interfaceName, string $methodName): Def...
method applyConverters (line 159) | private function applyConverters(AnnotationCollection $annotations, De...
FILE: src/ParameterHandler.php
type ParameterHandler (line 21) | interface ParameterHandler
method apply (line 30) | public function apply(RequestBuilder $requestBuilder, $value): void;
FILE: src/Proxy.php
type Proxy (line 19) | interface Proxy
method __handleRetrofitRequest (line 31) | public function __handleRetrofitRequest(string $interfaceName, string ...
FILE: src/Proxy/AbstractProxy.php
class AbstractProxy (line 21) | abstract class AbstractProxy implements Proxy
method __construct (line 45) | public function __construct(ServiceMethodFactory $serviceMethodFactory...
method __handleRetrofitRequest (line 62) | public function __handleRetrofitRequest(string $interfaceName, string ...
method createArgs (line 77) | private function createArgs(array $args, array $defaultArgs): array
FILE: src/ProxyFactory.php
type ProxyFactory (line 16) | interface ProxyFactory
method create (line 26) | public function create(string $service): ?Proxy;
FILE: src/RequestBodyConverter.php
type RequestBodyConverter (line 21) | interface RequestBodyConverter extends Converter
method convert (line 29) | public function convert($value): StreamInterface;
FILE: src/Response.php
type Response (line 21) | interface Response
method raw (line 28) | public function raw(): ResponseInterface;
method code (line 35) | public function code(): int;
method message (line 42) | public function message(): string;
method headers (line 49) | public function headers(): array;
method isSuccessful (line 56) | public function isSuccessful(): bool;
method body (line 63) | public function body();
method errorBody (line 70) | public function errorBody();
FILE: src/ResponseBodyConverter.php
type ResponseBodyConverter (line 21) | interface ResponseBodyConverter extends Converter
method convert (line 29) | public function convert(StreamInterface $value);
FILE: src/Retrofit.php
class Retrofit (line 19) | class Retrofit
method __construct (line 48) | public function __construct(ServiceResolver $serviceResolver, array $p...
method builder (line 59) | public static function builder(): RetrofitBuilder
method registerServices (line 70) | public function registerServices(array $services): void
method registerService (line 83) | public function registerService(string $service): void
method createAll (line 96) | public function createAll(string $srcDir): int
method createServices (line 110) | public function createServices(): int
method create (line 128) | public function create(string $service): Proxy
FILE: src/RetrofitBuilder.php
class RetrofitBuilder (line 35) | class RetrofitBuilder
method setCache (line 110) | public function setCache(CacheInterface $cache): RetrofitBuilder
method setCacheDir (line 123) | public function setCacheDir(string $cacheDir): RetrofitBuilder
method setHttpClient (line 136) | public function setHttpClient(HttpClient $client): RetrofitBuilder
method setBaseUrl (line 149) | public function setBaseUrl(string $baseUrl): RetrofitBuilder
method addCallAdapterFactory (line 162) | public function addCallAdapterFactory(CallAdapterFactory $callAdapterF...
method addConverterFactory (line 175) | public function addConverterFactory(ConverterFactory $converterFactory...
method addProxyFactory (line 188) | public function addProxyFactory(ProxyFactory $proxyFactory): RetrofitB...
method addAnnotationHandler (line 202) | public function addAnnotationHandler(string $annotationName, Annotatio...
method enableCache (line 215) | public function enableCache(bool $enable = true): RetrofitBuilder
method build (line 228) | public function build(): Retrofit
method createDefaultProxyFactory (line 247) | private function createDefaultProxyFactory(): ProxyFactory
FILE: src/ServiceMethodBuilder.php
type ServiceMethodBuilder (line 18) | interface ServiceMethodBuilder
method setMethod (line 26) | public function setMethod(string $method): ServiceMethodBuilder;
method setBaseUrl (line 34) | public function setBaseUrl(string $baseUrl): ServiceMethodBuilder;
method setPath (line 42) | public function setPath(string $path): ServiceMethodBuilder;
method setHasBody (line 51) | public function setHasBody(bool $hasBody): ServiceMethodBuilder;
method setContentType (line 60) | public function setContentType(string $contentType): ServiceMethodBuil...
method setIsJson (line 67) | public function setIsJson(): ServiceMethodBuilder;
method setIsFormUrlEncoded (line 74) | public function setIsFormUrlEncoded(): ServiceMethodBuilder;
method setIsMultipart (line 81) | public function setIsMultipart(): ServiceMethodBuilder;
method addHeader (line 90) | public function addHeader(string $name, string $header): ServiceMethod...
method addParameterHandler (line 99) | public function addParameterHandler(int $index, ParameterHandler $para...
method setCallAdapter (line 107) | public function setCallAdapter(CallAdapter $callAdapter): ServiceMetho...
FILE: src/StringConverter.php
type StringConverter (line 18) | interface StringConverter extends Converter
method convert (line 26) | public function convert($value): string;
FILE: tests/Mock/Unit/Internal/AnnotationProcessorTest/AnnotationProcessorTestMock.php
type AnnotationProcessorTestMock (line 19) | interface AnnotationProcessorTestMock
method foo (line 21) | public function foo(int $bar): Call;
method body (line 22) | public function body(StreamInterface $bar): Call;
method noType (line 23) | public function noType($bar): Call;
FILE: tests/Mock/Unit/Internal/AnnotationProcessorTest/BadConverterAnnotation.php
class BadConverterAnnotation (line 21) | class BadConverterAnnotation extends AbstractAnnotation implements Param...
method getVariableName (line 29) | public function getVariableName(): string
method converterType (line 41) | public function converterType(): ?string
FILE: tests/Mock/Unit/Internal/HttpClientCallTest/HttpClientCallTestClientMock.php
class HttpClientCallTestClientMock (line 21) | class HttpClientCallTestClientMock implements HttpClient
method __construct (line 43) | public function __construct(?ResponseInterface $response = null)
method send (line 55) | public function send(RequestInterface $request): ResponseInterface
method sendAsync (line 74) | public function sendAsync(RequestInterface $request, callable $onRespo...
method wait (line 85) | public function wait(): void
FILE: tests/Mock/Unit/Internal/HttpClientCallTest/HttpClientCallTestErrorBodyMock.php
class HttpClientCallTestErrorBodyMock (line 16) | class HttpClientCallTestErrorBodyMock
FILE: tests/Mock/Unit/Internal/HttpClientCallTest/HttpClientCallTestResponseBodyMock.php
class HttpClientCallTestResponseBodyMock (line 16) | class HttpClientCallTestResponseBodyMock
FILE: tests/Mock/Unit/Internal/HttpClientCallTest/HttpClientCallTestServiceMethodMock.php
class HttpClientCallTestServiceMethodMock (line 21) | class HttpClientCallTestServiceMethodMock implements ServiceMethod
method __construct (line 45) | public function __construct(
method toRequest (line 62) | public function toRequest(array $args): RequestInterface
method toResponseBody (line 73) | public function toResponseBody(ResponseInterface $response)
method toErrorBody (line 86) | public function toErrorBody(ResponseInterface $response)
method adapt (line 99) | public function adapt(Call $call)
method checkResponse (line 104) | private function checkResponse(ResponseInterface $response): void
FILE: tests/Mock/Unit/Internal/ProxyFactoryTest/PFTCTestCreate.php
type PFTCTestCreate (line 25) | interface PFTCTestCreate
method simple (line 40) | public function simple(string $path, stdClass $body, string &$query = ...
method static (line 47) | public static function static(): Call;
FILE: tests/Mock/Unit/Internal/ProxyFactoryTest/PFTCTestCreateCacheDirectoryFail.php
type PFTCTestCreateCacheDirectoryFail (line 25) | interface PFTCTestCreateCacheDirectoryFail
method simple (line 40) | public function simple(string $path, stdClass $body, string &$query = ...
method static (line 47) | public static function static(): Call;
FILE: tests/Mock/Unit/Internal/ProxyFactoryTest/PFTCTestCreateClientFail.php
type PFTCTestCreateClientFail (line 25) | interface PFTCTestCreateClientFail
method simple (line 40) | public function simple(string $path, stdClass $body, string &$query = ...
method static (line 47) | public static function static(): Call;
FILE: tests/Mock/Unit/Internal/ProxyFactoryTest/PFTCTestCreateTwice.php
type PFTCTestCreateTwice (line 25) | interface PFTCTestCreateTwice
method simple (line 40) | public function simple(string $path, stdClass $body, string &$query = ...
method static (line 47) | public static function static(): Call;
FILE: tests/Mock/Unit/Internal/ProxyFactoryTest/PFTCTestCreateWithoutCache.php
type PFTCTestCreateWithoutCache (line 25) | interface PFTCTestCreateWithoutCache
method simple (line 40) | public function simple(string $path, stdClass $body, string &$query = ...
method static (line 47) | public static function static(): Call;
FILE: tests/Mock/Unit/Internal/ProxyFactoryTest/ProxyFactoryTestClientNoReturnType.php
type ProxyFactoryTestClientNoReturnType (line 18) | interface ProxyFactoryTestClientNoReturnType
method foo (line 20) | public function foo(stdClass $foo);
FILE: tests/Mock/Unit/Internal/ProxyFactoryTest/ProxyFactoryTestClientNoTypehint.php
type ProxyFactoryTestClientNoTypehint (line 18) | interface ProxyFactoryTestClientNoTypehint
method foo (line 20) | public function foo($foo): Call;
FILE: tests/Mock/Unit/Internal/ProxyFactoryTest/ProxyFactoryTestFilesystem.php
class ProxyFactoryTestFilesystem (line 18) | class ProxyFactoryTestFilesystem extends Filesystem
method makeDirectory (line 27) | public function makeDirectory(string $pathname, int $mode = 0777, bool...
method put (line 33) | public function put(string $filename, string $contents): bool
FILE: tests/Mock/Unit/Internal/ProxyFactoryTest/ProxyFactoryTestHttpClient.php
class ProxyFactoryTestHttpClient (line 20) | class ProxyFactoryTestHttpClient implements HttpClient
method send (line 28) | public function send(RequestInterface $request): ResponseInterface
method sendAsync (line 47) | public function sendAsync(RequestInterface $request, callable $onRespo...
method wait (line 57) | public function wait(): void
FILE: tests/Mock/Unit/Internal/ServiceMethod/ServiceMethodFactoryTest/ServiceMethodFactoryTestClient.php
type ServiceMethodFactoryTestClient (line 23) | interface ServiceMethodFactoryTestClient
method foo (line 28) | public function foo(): Call;
method bar (line 33) | public function bar();
method baz (line 39) | public function baz(Body $body): Call;
method qux (line 46) | public function qux(): Call;
FILE: tests/Mock/Unit/Internal/ServiceMethod/ServiceMethodFactoryTest/ServiceMethodFactoryTestConverterFactory.php
class ServiceMethodFactoryTestConverterFactory (line 23) | class ServiceMethodFactoryTestConverterFactory implements ConverterFactory
method responseBodyConverter (line 31) | public function responseBodyConverter(TypeToken $type): ?ResponseBodyC...
method requestBodyConverter (line 47) | public function requestBodyConverter(TypeToken $type): ?RequestBodyCon...
method stringConverter (line 63) | public function stringConverter(TypeToken $type): ?StringConverter
FILE: tests/Mock/Unit/MockCall.php
class MockCall (line 20) | class MockCall implements Call
method execute (line 25) | public function execute(): Response
method enqueue (line 34) | public function enqueue(?callable $onResponse = null, ?callable $onFai...
method wait (line 41) | public function wait(): void
method request (line 48) | public function request(): RequestInterface
FILE: tests/Mock/Unit/MockConverterFactory.php
class MockConverterFactory (line 22) | class MockConverterFactory implements ConverterFactory
method responseBodyConverter (line 30) | public function responseBodyConverter(TypeToken $type): ?ResponseBodyC...
method requestBodyConverter (line 41) | public function requestBodyConverter(TypeToken $type): ?RequestBodyCon...
method stringConverter (line 52) | public function stringConverter(TypeToken $type): ?StringConverter
FILE: tests/Mock/Unit/RetrofitTest/ApiClient.php
type ApiClient (line 41) | interface ApiClient
method get (line 46) | public function get(): Call;
method uri (line 56) | public function uri(string $newUrl, array $queryMap, array $query1, bo...
method headers (line 69) | public function headers(array $headerMap, array $header1, int $header2...
method postWithoutBody (line 74) | public function postWithoutBody(): Call;
method body (line 81) | public function body(RetrofitTestRequestBodyMock $requestBody): Call;
method field (line 90) | public function field(float $field1, bool $field2, string $field3, arr...
method part (line 98) | public function part(RetrofitTestRequestBodyMock $part1, MultipartBody...
method callAdapter (line 103) | public function callAdapter(): RetrofitTestAdaptedCallMock;
method customAnnotation (line 109) | public function customAnnotation(): Call;
FILE: tests/Mock/Unit/RetrofitTest/CacheableApiClient.php
type CacheableApiClient (line 18) | interface CacheableApiClient extends ApiClient
FILE: tests/Mock/Unit/RetrofitTest/DefaultParamsApiClient.php
type DefaultParamsApiClient (line 22) | interface DefaultParamsApiClient
method getWithDefaults (line 41) | public function getWithDefaults(
FILE: tests/Mock/Unit/RetrofitTest/InvalidSyntaxApiClient.php
type InvalidSyntaxApiClient (line 20) | interface InvalidSyntaxApiClient
method get (line 26) | public function get(): Call;
FILE: tests/Mock/Unit/RetrofitTest/RetrofitTestAdaptedCallMock.php
class RetrofitTestAdaptedCallMock (line 16) | class RetrofitTestAdaptedCallMock
FILE: tests/Mock/Unit/RetrofitTest/RetrofitTestCallAdapterFactory.php
class RetrofitTestCallAdapterFactory (line 20) | class RetrofitTestCallAdapterFactory implements CallAdapterFactory
method supports (line 28) | public function supports(TypeToken $type): bool
method create (line 39) | public function create(TypeToken $type): CallAdapter
FILE: tests/Mock/Unit/RetrofitTest/RetrofitTestCallAdapterMock.php
class RetrofitTestCallAdapterMock (line 19) | class RetrofitTestCallAdapterMock implements CallAdapter
method adapt (line 27) | public function adapt(Call $call)
FILE: tests/Mock/Unit/RetrofitTest/RetrofitTestConverterFactory.php
class RetrofitTestConverterFactory (line 22) | class RetrofitTestConverterFactory implements ConverterFactory
method responseBodyConverter (line 30) | public function responseBodyConverter(TypeToken $type): ?ResponseBodyC...
method requestBodyConverter (line 41) | public function requestBodyConverter(TypeToken $type): ?RequestBodyCon...
method stringConverter (line 52) | public function stringConverter(TypeToken $type): ?StringConverter
FILE: tests/Mock/Unit/RetrofitTest/RetrofitTestCustomAnnotation.php
class RetrofitTestCustomAnnotation (line 21) | class RetrofitTestCustomAnnotation extends AbstractAnnotation
method init (line 23) | protected function init(): void
FILE: tests/Mock/Unit/RetrofitTest/RetrofitTestCustomAnnotationHandler.php
class RetrofitTestCustomAnnotationHandler (line 23) | class RetrofitTestCustomAnnotationHandler implements AnnotationHandler
method handle (line 34) | public function handle(
FILE: tests/Mock/Unit/RetrofitTest/RetrofitTestDelegateProxy.php
class RetrofitTestDelegateProxy (line 21) | class RetrofitTestDelegateProxy implements ApiClient, DefaultParamsApiCl...
method __construct (line 32) | public function __construct(Proxy $proxy)
method get (line 37) | public function get(): Call
method uri (line 42) | public function uri(string $newUrl, array $queryMap, array $query1, bo...
method headers (line 47) | public function headers(array $headerMap, array $header1, int $header2...
method postWithoutBody (line 55) | public function postWithoutBody(): Call
method body (line 60) | public function body(RetrofitTestRequestBodyMock $requestBody): Call
method field (line 65) | public function field(float $field1, bool $field2, string $field3, arr...
method part (line 70) | public function part(RetrofitTestRequestBodyMock $part1, MultipartBody...
method callAdapter (line 75) | public function callAdapter(): RetrofitTestAdaptedCallMock
method customAnnotation (line 80) | public function customAnnotation(): Call
method getWithDefaults (line 94) | public function getWithDefaults(
method __handleRetrofitRequest (line 120) | public function __handleRetrofitRequest(string $interfaceName, string ...
FILE: tests/Mock/Unit/RetrofitTest/RetrofitTestHttpClient.php
class RetrofitTestHttpClient (line 21) | class RetrofitTestHttpClient implements HttpClient
method send (line 34) | public function send(RequestInterface $request): ResponseInterface
method sendAsync (line 55) | public function sendAsync(RequestInterface $request, callable $onRespo...
method wait (line 65) | public function wait(): void
FILE: tests/Mock/Unit/RetrofitTest/RetrofitTestProxyFactory.php
class RetrofitTestProxyFactory (line 20) | class RetrofitTestProxyFactory implements ProxyFactory, DefaultProxyFact...
method create (line 35) | public function create(string $service): ?Proxy
method setDefaultProxyFactory (line 46) | public function setDefaultProxyFactory(ProxyFactory $proxyFactory): void
FILE: tests/Mock/Unit/RetrofitTest/RetrofitTestRequestBodyConverter.php
class RetrofitTestRequestBodyConverter (line 20) | class RetrofitTestRequestBodyConverter implements RequestBodyConverter
method convert (line 28) | public function convert($value): StreamInterface
FILE: tests/Mock/Unit/RetrofitTest/RetrofitTestRequestBodyMock.php
class RetrofitTestRequestBodyMock (line 16) | class RetrofitTestRequestBodyMock
FILE: tests/Mock/Unit/RetrofitTest/RetrofitTestResponseBodyConverter.php
class RetrofitTestResponseBodyConverter (line 19) | class RetrofitTestResponseBodyConverter implements ResponseBodyConverter
method convert (line 27) | public function convert(StreamInterface $value)
FILE: tests/Mock/Unit/RetrofitTest/RetrofitTestResponseBodyMock.php
class RetrofitTestResponseBodyMock (line 16) | class RetrofitTestResponseBodyMock
FILE: tests/Unit/Internal/AnnotationHandlersTest.php
class AnnotationHandlersTest (line 61) | class AnnotationHandlersTest extends TestCase
method setUp (line 78) | public function setUp()
method testHandleBodyAnnotation (line 86) | public function testHandleBodyAnnotation()
method testHandleBodyAnnotationWrongConverter (line 100) | public function testHandleBodyAnnotationWrongConverter()
method testHandleFieldAnnotation (line 117) | public function testHandleFieldAnnotation()
method testHandleFieldAnnotationWrongAnnotation (line 132) | public function testHandleFieldAnnotationWrongAnnotation()
method testHandleFieldAnnotationWrongConverter (line 149) | public function testHandleFieldAnnotationWrongConverter()
method testHandleFieldMapAnnotation (line 166) | public function testHandleFieldMapAnnotation()
method testHandleFieldMapAnnotationWrongAnnotation (line 181) | public function testHandleFieldMapAnnotationWrongAnnotation()
method testHandleFieldMapAnnotationWrongConverter (line 198) | public function testHandleFieldMapAnnotationWrongConverter()
method testHandleHeaderAnnotation (line 215) | public function testHandleHeaderAnnotation()
method testHandleHeaderAnnotationWrongConverter (line 228) | public function testHandleHeaderAnnotationWrongConverter()
method testHandleHeaderMapAnnotation (line 245) | public function testHandleHeaderMapAnnotation()
method testHandleHeaderMapAnnotationWrongConverter (line 258) | public function testHandleHeaderMapAnnotationWrongConverter()
method testHandleHeadersAnnotation (line 275) | public function testHandleHeadersAnnotation()
method testHandleHeadersAnnotationWrongConverter (line 287) | public function testHandleHeadersAnnotationWrongConverter()
method testHandleGETAnnotation (line 304) | public function testHandleGETAnnotation()
method testHandleGETAnnotationWrongConverter (line 318) | public function testHandleGETAnnotationWrongConverter()
method testHandlePOSTAnnotation (line 335) | public function testHandlePOSTAnnotation()
method testHandlePOSTAnnotationWrongAnnotation (line 348) | public function testHandlePOSTAnnotationWrongAnnotation()
method testHandlePartAnnotation (line 365) | public function testHandlePartAnnotation()
method testHandlePartAnnotationWrongAnnotation (line 380) | public function testHandlePartAnnotationWrongAnnotation()
method testHandlePartAnnotationWrongConverter (line 397) | public function testHandlePartAnnotationWrongConverter()
method testHandlePartMapAnnotation (line 414) | public function testHandlePartMapAnnotation()
method testHandlePartMapAnnotationWrongAnnotation (line 429) | public function testHandlePartMapAnnotationWrongAnnotation()
method testHandlePartMapAnnotationWrongConverter (line 446) | public function testHandlePartMapAnnotationWrongConverter()
method testHandlePathAnnotation (line 463) | public function testHandlePathAnnotation()
method testHandlePathAnnotationWrongConverter (line 476) | public function testHandlePathAnnotationWrongConverter()
method testHandleQueryAnnotation (line 493) | public function testHandleQueryAnnotation()
method testHandleQueryAnnotationWrongAnnotation (line 506) | public function testHandleQueryAnnotationWrongAnnotation()
method testHandleQueryAnnotationWrongConverter (line 523) | public function testHandleQueryAnnotationWrongConverter()
method testHandleQueryMapAnnotation (line 540) | public function testHandleQueryMapAnnotation()
method testHandleQueryMapAnnotationWrongAnnotation (line 553) | public function testHandleQueryMapAnnotationWrongAnnotation()
method testHandleQueryMapAnnotationWrongConverter (line 570) | public function testHandleQueryMapAnnotationWrongConverter()
method testHandleQueryNameAnnotation (line 587) | public function testHandleQueryNameAnnotation()
method testHandleQueryNameAnnotationWrongAnnotation (line 600) | public function testHandleQueryNameAnnotationWrongAnnotation()
method testHandleQueryNameAnnotationWrongConverter (line 617) | public function testHandleQueryNameAnnotationWrongConverter()
method testHandleUrlAnnotation (line 634) | public function testHandleUrlAnnotation()
method testHandleUrlAnnotationWrongConverter (line 647) | public function testHandleUrlAnnotationWrongConverter()
FILE: tests/Unit/Internal/AnnotationProcessorTest.php
class AnnotationProcessorTest (line 32) | class AnnotationProcessorTest extends TestCase
method setUp (line 49) | public function setUp()
method testParameterAwareAnnotation (line 62) | public function testParameterAwareAnnotation()
method testNonParameterAwareAnnotation (line 78) | public function testNonParameterAwareAnnotation()
method testRequestBodyAnnotation (line 94) | public function testRequestBodyAnnotation()
method testNoHandler (line 110) | public function testNoHandler()
method testParameterNotFound (line 126) | public function testParameterNotFound()
method testParameterTypeNotFound (line 148) | public function testParameterTypeNotFound()
method testConverterNotFound (line 169) | public function testConverterNotFound()
FILE: tests/Unit/Internal/CallAdapterTest.php
class CallAdapterTest (line 23) | class CallAdapterTest extends TestCase
method setUp (line 30) | public function setUp()
method testAdaptDefaultCall (line 35) | public function testAdaptDefaultCall()
method testCallAdapterNotFound (line 43) | public function testCallAdapterNotFound()
FILE: tests/Unit/Internal/ConverterTest.php
class ConverterTest (line 25) | class ConverterTest extends TestCase
method setUp (line 32) | public function setUp()
method testRequestBodyConverter (line 37) | public function testRequestBodyConverter()
method testRequestBodyConverterProviderCache (line 45) | public function testRequestBodyConverterProviderCache()
method testRequestBodyConverterProviderException (line 53) | public function testRequestBodyConverterProviderException()
method testResponseBodyConverter (line 66) | public function testResponseBodyConverter()
method testResponseBodyConverterProviderCache (line 74) | public function testResponseBodyConverterProviderCache()
method testResponseBodyConverterProviderException (line 82) | public function testResponseBodyConverterProviderException()
method testStringConverter (line 94) | public function testStringConverter()
method testStringConverterInt (line 101) | public function testStringConverterInt()
method testStringConverterFloat (line 108) | public function testStringConverterFloat()
method testStringConverterTrue (line 115) | public function testStringConverterTrue()
method testStringConverterFalse (line 122) | public function testStringConverterFalse()
method testStringConverterArray (line 129) | public function testStringConverterArray()
method testStringConverterObject (line 136) | public function testStringConverterObject()
method testStringConverterProviderCache (line 143) | public function testStringConverterProviderCache()
method testStringConverterProviderException (line 151) | public function testStringConverterProviderException()
FILE: tests/Unit/Internal/DefaultProxyFactoryTest.php
class DefaultProxyFactoryTest (line 35) | class DefaultProxyFactoryTest extends TestCase
method setUp (line 42) | public function setUp()
method testCreate (line 47) | public function testCreate()
method testCreateTwice (line 63) | public function testCreateTwice()
method testCreateWithoutCache (line 79) | public function testCreateWithoutCache()
method testCreateInvalidInterface (line 90) | public function testCreateInvalidInterface()
method testCreateNoTypehint (line 102) | public function testCreateNoTypehint()
method testCreateNoReturnType (line 118) | public function testCreateNoReturnType()
method testCreateCacheDirectoryFail (line 134) | public function testCreateCacheDirectoryFail()
method testCreateClientFail (line 151) | public function testCreateClientFail()
method createFactory (line 168) | private function createFactory(bool $enableCache = true): DefaultProxy...
method getExpectedClass (line 189) | private function getExpectedClass(): string
FILE: tests/Unit/Internal/FilesystemTest.php
class FilesystemTest (line 13) | class FilesystemTest extends TestCase
method setUp (line 20) | public function setUp()
method testCreateDirectory (line 26) | public function testCreateDirectory()
method testCreateFile (line 33) | public function testCreateFile()
FILE: tests/Unit/Internal/HttpClientCallTest.php
class HttpClientCallTest (line 22) | class HttpClientCallTest extends TestCase
method testSync (line 24) | public function testSync()
method testSyncError (line 44) | public function testSyncError()
method testAsync (line 64) | public function testAsync()
method testAsyncError (line 91) | public function testAsyncError()
method testAsyncFailure (line 118) | public function testAsyncFailure()
method testAsyncFailureThrowsException (line 140) | public function testAsyncFailureThrowsException()
method testSyncInvalidJsonResponseThrowsException (line 166) | public function testSyncInvalidJsonResponseThrowsException()
method testSyncInvalidJsonErrorThrowsException (line 190) | public function testSyncInvalidJsonErrorThrowsException()
method testAsyncInvalidJsonResponseThrowsExceptions (line 214) | public function testAsyncInvalidJsonResponseThrowsExceptions()
method testAsyncInvalidJsonerrorThrowsExceptions (line 239) | public function testAsyncInvalidJsonerrorThrowsExceptions()
FILE: tests/Unit/Internal/ParameterHandlersTest.php
class ParameterHandlersTest (line 39) | class ParameterHandlersTest extends TestCase
method setUp (line 46) | public function setUp()
method testBodyHandler (line 51) | public function testBodyHandler()
method testBodyHandlerNull (line 59) | public function testBodyHandlerNull()
method testFieldMapHandler (line 66) | public function testFieldMapHandler()
method testFieldMapHandlerIterator (line 84) | public function testFieldMapHandlerIterator()
method testFieldMapHandlerEmpty (line 102) | public function testFieldMapHandlerEmpty()
method testFieldMapHandlerNull (line 109) | public function testFieldMapHandlerNull()
method testFieldHandler (line 116) | public function testFieldHandler()
method testFieldHandlerArray (line 123) | public function testFieldHandlerArray()
method testFieldHandlerNull (line 130) | public function testFieldHandlerNull()
method testHeaderMapHandler (line 137) | public function testHeaderMapHandler()
method testHeaderMapHandlerIterator (line 154) | public function testHeaderMapHandlerIterator()
method testHeaderMapHandlerEmpty (line 171) | public function testHeaderMapHandlerEmpty()
method testHeaderMapHandlerNull (line 178) | public function testHeaderMapHandlerNull()
method testHeaderHandler (line 185) | public function testHeaderHandler()
method testHeaderHandlerArray (line 192) | public function testHeaderHandlerArray()
method testHeaderHandlerNull (line 199) | public function testHeaderHandlerNull()
method testPartMapHandler (line 206) | public function testPartMapHandler()
method testPartMapHandlerIterator (line 247) | public function testPartMapHandlerIterator()
method testPartMapHandlerEmpty (line 288) | public function testPartMapHandlerEmpty()
method testPartMapHandlerNull (line 295) | public function testPartMapHandlerNull()
method testPartHandler (line 302) | public function testPartHandler()
method testPartHandlerMultipart (line 316) | public function testPartHandlerMultipart()
method testPartHandlerMultipartHeadersAndFilename (line 331) | public function testPartHandlerMultipartHeadersAndFilename()
method testPartHandlerNull (line 346) | public function testPartHandlerNull()
method testPathHandler (line 353) | public function testPathHandler()
method testPathHandlerNull (line 360) | public function testPathHandlerNull()
method testQueryMapHandler (line 372) | public function testQueryMapHandler()
method testQueryMapHandlerIterator (line 389) | public function testQueryMapHandlerIterator()
method testQueryMapHandlerEmpty (line 406) | public function testQueryMapHandlerEmpty()
method testQueryMapHandlerNull (line 413) | public function testQueryMapHandlerNull()
method testQueryNameHandler (line 420) | public function testQueryNameHandler()
method testQueryNameHandlerArray (line 427) | public function testQueryNameHandlerArray()
method testQueryNameHandlerNull (line 434) | public function testQueryNameHandlerNull()
method testQueryHandler (line 441) | public function testQueryHandler()
method testQueryHandlerArray (line 448) | public function testQueryHandlerArray()
method testQueryHandlerNull (line 455) | public function testQueryHandlerNull()
method testUrlHandler (line 462) | public function testUrlHandler()
method testUrlHandlerNull (line 469) | public function testUrlHandlerNull()
method testUsingMapAsListThrowsException (line 481) | public function testUsingMapAsListThrowsException()
FILE: tests/Unit/Internal/RequestBuilderTest.php
class RequestBuilderTest (line 15) | class RequestBuilderTest extends TestCase
method testSimple (line 17) | public function testSimple()
method testSetBody (line 28) | public function testSetBody()
method testUrlSetters (line 41) | public function testUrlSetters()
method testUrlSettersEncoded (line 56) | public function testUrlSettersEncoded()
method testAddHeader (line 71) | public function testAddHeader()
method testFields (line 83) | public function testFields()
method testFieldsEncoded (line 96) | public function testFieldsEncoded()
method testParts (line 109) | public function testParts()
method testPartsFilenameAndHeader (line 123) | public function testPartsFilenameAndHeader()
method testFieldAndBody (line 138) | public function testFieldAndBody()
method testPartAndBody (line 154) | public function testPartAndBody()
FILE: tests/Unit/Internal/RetrofitResponseTest.php
class RetrofitResponseTest (line 14) | class RetrofitResponseTest extends TestCase
method testGettersSuccess (line 16) | public function testGettersSuccess()
method testGettersFailure (line 31) | public function testGettersFailure()
FILE: tests/Unit/Internal/ServiceMethod/DefaultServiceMethodBuilderTest.php
class DefaultServiceMethodBuilderTest (line 20) | class DefaultServiceMethodBuilderTest extends TestCase
method setUp (line 27) | public function setUp()
method testCreateServiceMethodGet (line 32) | public function testCreateServiceMethodGet()
method testCreateServiceMethodPost (line 48) | public function testCreateServiceMethodPost()
method testCreateServiceMethodForm (line 69) | public function testCreateServiceMethodForm()
method testCreateServiceMethodMultipart (line 90) | public function testCreateServiceMethodMultipart()
method testSetMethodTwice (line 111) | public function testSetMethodTwice()
method testSetHasBodyAfterSet (line 127) | public function testSetHasBodyAfterSet()
method testChangeContentType (line 145) | public function testChangeContentType()
method testBuildWithoutMethod (line 163) | public function testBuildWithoutMethod()
method testBuildWithoutBaseUrl (line 178) | public function testBuildWithoutBaseUrl()
method testBuildWithoutPath (line 194) | public function testBuildWithoutPath()
method testBuildWithoutContentType (line 211) | public function testBuildWithoutContentType()
method testBuildWithoutBody (line 231) | public function testBuildWithoutBody()
method testBuildWithoutResponseConverter (line 252) | public function testBuildWithoutResponseConverter()
method testBuildWithoutErrorConverter (line 271) | public function testBuildWithoutErrorConverter()
method testBuildWithoutCallAdapter (line 291) | public function testBuildWithoutCallAdapter()
method testBuildWillNotOverrideContentType (line 312) | public function testBuildWillNotOverrideContentType()
FILE: tests/Unit/Internal/ServiceMethod/ServiceMethodFactoryTest.php
class ServiceMethodFactoryTest (line 28) | class ServiceMethodFactoryTest extends TestCase
method setUp (line 35) | public function setUp()
method testCreateServiceMethod (line 49) | public function testCreateServiceMethod()
method testCreateServiceMethodCustomConverters (line 62) | public function testCreateServiceMethodCustomConverters()
method testCreateServiceMethodNoReturnType (line 75) | public function testCreateServiceMethodNoReturnType()
method testCreateServiceMethodAnnotationException (line 91) | public function testCreateServiceMethodAnnotationException()
FILE: tests/Unit/Internal/ServiceMethod/ServiceMethodTest.php
class ServiceMethodTest (line 21) | class ServiceMethodTest extends TestCase
method setUp (line 28) | public function setUp()
method testCreateRequest (line 42) | public function testCreateRequest()
method testCreateRequestDifferentParameters (line 57) | public function testCreateRequestDifferentParameters()
method testGetResponseBody (line 74) | public function testGetResponseBody()
method testGetErrorBody (line 83) | public function testGetErrorBody()
method testAdaptCall (line 92) | public function testAdaptCall()
FILE: tests/Unit/RetrofitTest.php
class RetrofitTest (line 32) | class RetrofitTest extends TestCase
method setUp (line 44) | public function setUp()
method testSimple (line 52) | public function testSimple()
method testUri (line 70) | public function testUri()
method testHeaders (line 96) | public function testHeaders()
method testPostWithoutBody (line 130) | public function testPostWithoutBody()
method testBody (line 148) | public function testBody()
method testField (line 174) | public function testField()
method testPart (line 193) | public function testPart()
method testCallAdapter (line 243) | public function testCallAdapter()
method testCustomProxy (line 257) | public function testCustomProxy()
method testCustomAnnotation (line 277) | public function testCustomAnnotation()
method testGetWithDefaults (line 298) | public function testGetWithDefaults()
method testGetWithSomeDefaults (line 317) | public function testGetWithSomeDefaults()
method testGetWithNullDefaults (line 336) | public function testGetWithNullDefaults()
method testCache (line 355) | public function testCache()
method testCustomCache (line 382) | public function testCustomCache()
method testBuilderThrowsExceptionWithoutBaseUrl (line 399) | public function testBuilderThrowsExceptionWithoutBaseUrl()
method testBuilderThrowsExceptionWithoutHttpClient (line 411) | public function testBuilderThrowsExceptionWithoutHttpClient()
method testBuilderThrowsExceptionWithoutCacheDir (line 425) | public function testBuilderThrowsExceptionWithoutCacheDir()
method testCreateServices (line 437) | public function testCreateServices()
method testCreateAll (line 445) | public function testCreateAll()
method testCreateThrowsExceptionWithoutFactory (line 452) | public function testCreateThrowsExceptionWithoutFactory()
method testCreateThrowsExceptionWithInvalidHeaderSyntax (line 465) | public function testCreateThrowsExceptionWithInvalidHeaderSyntax()
Condensed preview — 164 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (399K chars).
[
{
"path": ".gitignore",
"chars": 59,
"preview": "/vendor\n/composer.lock\n/tests/cache\n/build\n/.php_cs.cache\n\n"
},
{
"path": ".travis.yml",
"chars": 675,
"preview": "sudo: false\n\nlanguage: php\nphp:\n - 7.1\n - 7.2\n - 7.3\n - 7.4\n\nenv:\n - SF_CACHE=3\n - SF_CACHE=4\n - SF_CACHE=4.3\n -"
},
{
"path": "CHANGELOG.md",
"chars": 2665,
"preview": "Change Log\n==========\n\nThis document keeps track of important changes between releases of the library.\n\n3.2.0\n-----\n\n* A"
},
{
"path": "CONTRIBUTING.md",
"chars": 333,
"preview": "Contributing Guidelines\n=======================\n\nBefore contributing code, please be aware of the following:\n\nCode Style"
},
{
"path": "CONTRIBUTORS.md",
"chars": 452,
"preview": "Contributors\n============\n\nThis is a credits file of people that have contributed Retrofit-PHP Library.\n\n * Nate Brunett"
},
{
"path": "LICENSE.md",
"chars": 1113,
"preview": "Project License\n===============\n\nThe MIT License (MIT)\n\nCopyright (c) 2015 Nate Brunette\n\nPermission is hereby granted, "
},
{
"path": "README.md",
"chars": 3812,
"preview": "Retrofit PHP\n============\n\n[](https://travis-"
},
{
"path": "bin/retrofit",
"chars": 577,
"preview": "#!/usr/bin/env php\n\n<?php\n\nuse Doctrine\\Common\\Annotations\\AnnotationRegistry;\nuse Tebru\\Retrofit\\Command\\CompileCommand"
},
{
"path": "composer.json",
"chars": 959,
"preview": "{\n \"name\": \"tebru/retrofit-php\",\n \"description\": \"Retrofit for PHP - A type-safe PHP REST client.\",\n \"require\":"
},
{
"path": "docs/advanced_usage.md",
"chars": 3191,
"preview": "Advanced Usage\n==============\n\nCaching\n-------\n\nBy default, Retrofit doesn't do any filesystem caching. In production,\ny"
},
{
"path": "docs/annotations.md",
"chars": 8252,
"preview": "Annotation Reference\n====================\n\nA simple example will be provided to show usage for each annotation,\nbut may "
},
{
"path": "docs/installation.md",
"chars": 1008,
"preview": "Installation\n============\n\nInstallation with Composer\n--------------------------\n\n```bash\ncomposer require tebru/retrofi"
},
{
"path": "docs/upgrade_2_3.md",
"chars": 6059,
"preview": "Upgrading from Retrofit 2 to Retrofit 3\n=======================================\n\nRetrofit 3 is a complete rewrite of Ret"
},
{
"path": "docs/usage.md",
"chars": 7765,
"preview": "Usage\n=====\n\nTo start, it would be a good idea to get an understanding of what\nRetrofit is doing under the hood.\n\nLet's "
},
{
"path": "phpcs.xml",
"chars": 90,
"preview": "<?xml version=\"1.0\"?>\n<ruleset>\n <file>./src</file>\n <rule ref=\"PSR1\" />\n</ruleset>\n"
},
{
"path": "phpunit.xml",
"chars": 467,
"preview": "<phpunit colors=\"true\" bootstrap=\"tests/bootstrap.php\">\n <testsuites>\n <testsuite name=\"Unit\">\n <di"
},
{
"path": "src/Annotation/Body.php",
"chars": 844,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/DELETE.php",
"chars": 829,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/Encodable.php",
"chars": 1188,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/ErrorBody.php",
"chars": 458,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/Field.php",
"chars": 809,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/FieldMap.php",
"chars": 789,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/GET.php",
"chars": 820,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/HEAD.php",
"chars": 823,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/Header.php",
"chars": 993,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/HeaderMap.php",
"chars": 1005,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Tebru\\Retrofit\\Annotation;\n\nuse Tebru\\Retrofit\\StringConverter;\n\n/**\n * Repre"
},
{
"path": "src/Annotation/Headers.php",
"chars": 1220,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/HttpRequest.php",
"chars": 734,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/OPTIONS.php",
"chars": 832,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/PATCH.php",
"chars": 825,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/POST.php",
"chars": 822,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/PUT.php",
"chars": 819,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/ParameterAnnotation.php",
"chars": 936,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/ParameterAwareAnnotation.php",
"chars": 944,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/Part.php",
"chars": 1674,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/PartMap.php",
"chars": 1727,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/Path.php",
"chars": 1185,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/Query.php",
"chars": 800,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/QueryMap.php",
"chars": 789,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/QueryName.php",
"chars": 638,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/REQUEST.php",
"chars": 1359,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/ResponseBody.php",
"chars": 464,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Annotation/Url.php",
"chars": 693,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/AnnotationHandler.php",
"chars": 1202,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Call.php",
"chars": 1831,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/CallAdapter.php",
"chars": 548,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/CallAdapterFactory.php",
"chars": 675,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Command/CompileCommand.php",
"chars": 3456,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Converter.php",
"chars": 333,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/ConverterFactory.php",
"chars": 1247,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/DefaultProxyFactoryAware.php",
"chars": 775,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Exception/ResponseHandlingFailedException.php",
"chars": 1676,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Finder/ServiceResolver.php",
"chars": 1957,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Http/MultipartBody.php",
"chars": 1545,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/HttpClient.php",
"chars": 1523,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/AnnotationHandler/BodyAnnotHandler.php",
"chars": 1790,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/AnnotationHandler/FieldAnnotHandler.php",
"chars": 2109,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/AnnotationHandler/FieldMapAnnotHandler.php",
"chars": 2096,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/AnnotationHandler/HeaderAnnotHandler.php",
"chars": 1737,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/AnnotationHandler/HeaderMapAnnotHandler.php",
"chars": 1699,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/AnnotationHandler/HeadersAnnotHandler.php",
"chars": 1653,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/AnnotationHandler/HttpRequestAnnotHandler.php",
"chars": 2016,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/AnnotationHandler/PartAnnotHandler.php",
"chars": 2065,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/AnnotationHandler/PartMapAnnotHandler.php",
"chars": 2009,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/AnnotationHandler/PathAnnotHandler.php",
"chars": 1694,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/AnnotationHandler/QueryAnnotHandler.php",
"chars": 1993,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/AnnotationHandler/QueryMapAnnotHandler.php",
"chars": 1988,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/AnnotationHandler/QueryNameAnnotHandler.php",
"chars": 1997,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/AnnotationHandler/UrlAnnotHandler.php",
"chars": 1662,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/AnnotationProcessor.php",
"chars": 4977,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/CacheProvider.php",
"chars": 2212,
"preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Tebru\\Retrofit\\Internal;\n\nuse Psr\\SimpleCache\\CacheInterface;\nuse Symfony\\Com"
},
{
"path": "src/Internal/CallAdapter/CallAdapterProvider.php",
"chars": 1396,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/CallAdapter/DefaultCallAdapter.php",
"chars": 648,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/CallAdapter/DefaultCallAdapterFactory.php",
"chars": 979,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/Converter/ConverterProvider.php",
"chars": 3968,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/Converter/DefaultConverterFactory.php",
"chars": 1822,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/Converter/DefaultRequestBodyConverter.php",
"chars": 715,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/Converter/DefaultResponseBodyConverter.php",
"chars": 713,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/Converter/DefaultStringConverter.php",
"chars": 884,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/Converter/NoopStringConverter.php",
"chars": 640,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/DefaultProxyFactory.php",
"chars": 10289,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/Filesystem.php",
"chars": 1011,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/HttpClientCall.php",
"chars": 4879,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ParameterHandler/AbstractParameterHandler.php",
"chars": 2487,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ParameterHandler/BodyParamHandler.php",
"chars": 1117,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ParameterHandler/FieldMapParamHandler.php",
"chars": 1422,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ParameterHandler/FieldParamHandler.php",
"chars": 1484,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ParameterHandler/HeaderMapParamHandler.php",
"chars": 1295,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ParameterHandler/HeaderParamHandler.php",
"chars": 1337,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ParameterHandler/PartMapParamHandler.php",
"chars": 1354,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ParameterHandler/PartParamHandler.php",
"chars": 1404,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ParameterHandler/PathParamHandler.php",
"chars": 1334,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ParameterHandler/QueryMapParamHandler.php",
"chars": 1442,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ParameterHandler/QueryNameParamHandler.php",
"chars": 1358,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ParameterHandler/QueryParamHandler.php",
"chars": 1484,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ParameterHandler/UrlParamHandler.php",
"chars": 1192,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/RequestBuilder.php",
"chars": 5592,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/RetrofitResponse.php",
"chars": 2355,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ServiceMethod/DefaultServiceMethod.php",
"chars": 4617,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ServiceMethod/DefaultServiceMethodBuilder.php",
"chars": 10199,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ServiceMethod/ServiceMethodFactory.php",
"chars": 6232,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Internal/ServiceMethod.php",
"chars": 1390,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/ParameterHandler.php",
"chars": 700,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Proxy/AbstractProxy.php",
"chars": 2447,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Proxy.php",
"chars": 834,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/ProxyFactory.php",
"chars": 533,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/RequestBodyConverter.php",
"chars": 597,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Response.php",
"chars": 1263,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/ResponseBodyConverter.php",
"chars": 613,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/Retrofit.php",
"chars": 3228,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/RetrofitBuilder.php",
"chars": 9348,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/ServiceMethodBuilder.php",
"chars": 2959,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "src/StringConverter.php",
"chars": 499,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/AnnotationProcessorTest/AnnotationProcessorTestMock.php",
"chars": 553,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/AnnotationProcessorTest/BadConverterAnnotation.php",
"chars": 1079,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/HttpClientCallTest/HttpClientCallTestClientMock.php",
"chars": 2370,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/HttpClientCallTest/HttpClientCallTestErrorBodyMock.php",
"chars": 350,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/HttpClientCallTest/HttpClientCallTestResponseBodyMock.php",
"chars": 356,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/HttpClientCallTest/HttpClientCallTestServiceMethodMock.php",
"chars": 2597,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/ProxyFactoryTest/PFTCTestCreate.php",
"chars": 1084,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/ProxyFactoryTest/PFTCTestCreateCacheDirectoryFail.php",
"chars": 1112,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/ProxyFactoryTest/PFTCTestCreateClientFail.php",
"chars": 1096,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/ProxyFactoryTest/PFTCTestCreateTwice.php",
"chars": 1086,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/ProxyFactoryTest/PFTCTestCreateWithoutCache.php",
"chars": 1100,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/ProxyFactoryTest/ProxyFactoryTestClientNoReturnType.php",
"chars": 416,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/ProxyFactoryTest/ProxyFactoryTestClientNoTypehint.php",
"chars": 420,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/ProxyFactoryTest/ProxyFactoryTestFilesystem.php",
"chars": 910,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/ProxyFactoryTest/ProxyFactoryTestHttpClient.php",
"chars": 1721,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/ServiceMethod/ServiceMethodFactoryTest/ServiceMethodFactoryTestClient.php",
"chars": 944,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/Internal/ServiceMethod/ServiceMethodFactoryTest/ServiceMethodFactoryTestConverterFactory.php",
"chars": 1898,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/MockCall.php",
"chars": 879,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/MockConverterFactory.php",
"chars": 1279,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/ApiClient.php",
"chars": 2920,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/CacheableApiClient.php",
"chars": 370,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/DefaultParamsApiClient.php",
"chars": 1120,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/InvalidSyntaxApiClient.php",
"chars": 524,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/RetrofitTestAdaptedCallMock.php",
"chars": 327,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/RetrofitTestCallAdapterFactory.php",
"chars": 955,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/RetrofitTestCallAdapterMock.php",
"chars": 645,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/RetrofitTestConverterFactory.php",
"chars": 1377,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/RetrofitTestCustomAnnotation.php",
"chars": 502,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/RetrofitTestCustomAnnotationHandler.php",
"chars": 1405,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/RetrofitTestDelegateProxy.php",
"chars": 3564,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/RetrofitTestHttpClient.php",
"chars": 1782,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/RetrofitTestProxyFactory.php",
"chars": 1161,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/RetrofitTestRequestBodyConverter.php",
"chars": 768,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/RetrofitTestRequestBodyMock.php",
"chars": 360,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/RetrofitTestResponseBodyConverter.php",
"chars": 687,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Mock/Unit/RetrofitTest/RetrofitTestResponseBodyMock.php",
"chars": 330,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Unit/Internal/AnnotationHandlersTest.php",
"chars": 23375,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Unit/Internal/AnnotationProcessorTest.php",
"chars": 6461,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Unit/Internal/CallAdapterTest.php",
"chars": 1511,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Unit/Internal/ConverterTest.php",
"chars": 5231,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Unit/Internal/DefaultProxyFactoryTest.php",
"chars": 7920,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Unit/Internal/FilesystemTest.php",
"chars": 981,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Unit/Internal/HttpClientCallTest.php",
"chars": 8833,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Unit/Internal/ParameterHandlersTest.php",
"chars": 16725,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Unit/Internal/RequestBuilderTest.php",
"chars": 7198,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Unit/Internal/RetrofitResponseTest.php",
"chars": 1673,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Unit/Internal/ServiceMethod/DefaultServiceMethodBuilderTest.php",
"chars": 13622,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Unit/Internal/ServiceMethod/ServiceMethodFactoryTest.php",
"chars": 4970,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Unit/Internal/ServiceMethod/ServiceMethodTest.php",
"chars": 2942,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/Unit/RetrofitTest.php",
"chars": 16161,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
},
{
"path": "tests/bootstrap.php",
"chars": 318,
"preview": "<?php\n/*\n * Copyright (c) Nate Brunette.\n * Distributed under the MIT License (http://opensource.org/licenses/MIT)\n */\n\n"
}
]
About this extraction
This page contains the full source code of the tebru/retrofit-php GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 164 files (362.3 KB), approximately 90.3k tokens, and a symbol index with 663 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.