PHPackages                             kix/apiranha - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. kix/apiranha

ActiveLibrary

kix/apiranha
============

An API client for PHP

07[4 issues](https://github.com/kix/apiranha/issues)[1 PRs](https://github.com/kix/apiranha/pulls)PHP

Since Apr 5Pushed 7y agoCompare

[ Source](https://github.com/kix/apiranha)[ Packagist](https://packagist.org/packages/kix/apiranha)[ RSS](/packages/kix-apiranha/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependenciesVersions (3)Used By (0)

Apiranha
========

[](#apiranha)

Apiranha is a library that makes consuming APIs easier and faster. Some of the inspiration for Apiranha comes from [Retrofit](square.github.io/retrofit/)

[![Travis Ci](https://camo.githubusercontent.com/d99d77abc9d78fe63eba8382fa11689e8cd2f9665a65a953d0b8eae28adf7077/68747470733a2f2f7472617669732d63692e6f72672f6b69782f61706972616e68612e7376673f6272616e63683d6d6173746572)](https://travis-ci.org/kix/apiranha)[![Code Climate](https://camo.githubusercontent.com/52721f8c183d862be9d77cc289c7558dadaaf0d5d7ed98508af6f08a382f5946/68747470733a2f2f636f6465636c696d6174652e636f6d2f6769746875622f6b69782f61706972616e68612f6261646765732f6770612e737667)](https://codeclimate.com/github/kix/apiranha)[![Coverage Status](https://camo.githubusercontent.com/18ce391ee1449b367e47a8ccaddb8068039e46830d309cbd6baa81fb14c3dbf3/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f6b69782f61706972616e68612f62616467652e7376673f6272616e63683d6d6173746572)](https://coveralls.io/github/kix/apiranha?branch=master)

Example usage
-------------

[](#example-usage)

A complete example can be found at `examples/`. See [`ExampleCommand`](https://github.com/kix/apiranha/blob/master/examples/Command/ExampleCommand.php) and [`BuilderExampleCommand`](https://github.com/kix/apiranha/blob/master/examples/Command/BuilderExampleCommand.php).

Quick start
-----------

[](#quick-start)

Make sure you have Composer installed, then run this in your project folder:

```
composer require kix/apiranha='dev-master'

```

Also, you'll need to require `guzzlehttp/guzzle` if you don't want to bring your own implementation of an HTTP client.

In order to consume [Github API](https://developer.github.com/v3/), for example, first you need to declare your endpoints in an interface and annotate those. The annotations available under the `Kix\Apiranha\Annotation` namespace are:

- HTTP/REST method annotations:
    - `Get`
    - `CGet`
    - `Delete`
    - `Post`
    - `Put`
- `Returns`, a special annotation to denote the endpoint return type.

First, you need to decide which data you actually need from the endpoint. In our case, we're just getting the ID, name, language and stargazers count from the Github REST API:

```
namespace \Kix\Apiranha\Examples\Model;

class Repository
{
    private $id;
    private $name;
    private $language;
    private $stargazersCount;
}
```

Of course, it will be necessary for you to read the properties, so you could either declare the fields as `public`, or you could add getters for the fields.

Next, to enable getting a single repo from the Github API, you would declare an interface like this (note the annotations):

```
use Kix\Apiranha\Annotation as Rest;

interface GithubApi
{
    /**
     * @Rest\Returns("\Kix\Apiranha\Examples\Model\Repository")
     * @Rest\Get("/repos/{username}/{repo}")
     */
    public function getRepo(string $username, string $repo);
}
```

Here, we say that the `getRepo` method will be returning data from the [`/repos` endpoint](https://developer.github.com/v3/repos/#list-user-repositories). Note that the URL parameters are consistently named and also typehinted with `string`: this will help us generate a correct URL for a specific call.

After you've implemented your endpoint interface and your model, it's time for the magic to happen:

```
use Kix\Apiranha\Builder;
use Kix\Apiranha\Examples\Definition\GithubApi;

/** @var $endpoint GithubApi */
$endpoint = Builder::createEndpoint('http://api.github.com', [GithubApi::class]);
```

Calling `Builder::createEndpoint`, you get back an object that represents your API in terms of the interface you have declared previously. Which, in turn, means you can annotate it with `@var $endpoint GithubApi`.

Since now, all the methods you have declared in the interface become available via the endpoint object. Here's how it looks:

```
 > $endpoint->listRepos('kix');

=> array(30) {
=>   object(Kix\Apiranha\Examples\Model\Repository)#76 (4) {
=>     ["id":"Kix\Apiranha\Examples\Model\Repository":private]=>
=>     int(43456580)
=>     ["name":"Kix\Apiranha\Examples\Model\Repository":private]=>
=>     string(6) "apiranha"
=>     ["language":"Kix\Apiranha\Examples\Model\Repository":private]=>
=>     NULL
=>     ["stargazersCount":"Kix\Apiranha\Examples\Model\Repository":private]=>
=>     int(1)
=>   }
=>   ...
=> }
```

Digging deeper
==============

[](#digging-deeper)

Well, what happens under the hood? The `Builder` you've used in this simple API client is just a facade that helps you do stuff quick, and hides a lot of implementation details you might not actually care about. However, there's more to it than just calling a method and getting a result back. Here's what the builder presumes:

- Your routes will always be declared in the annotations you provide, and the parameters will be bound to the URIs as-is;
- You will be using Guzzle as the HTTP client (which this package will suggest upon installation)
- The API serialization format will be JSON, handled by Symfony's serializer
- Model instances your API will return will be hydrated using PHP's reflection API (which could be costly)

If some of these statements do not comply with your requirements, you're always free to extend the logic behind Apiranha.

Builder under the hood
----------------------

[](#builder-under-the-hood)

Let's take a look at `Builder`'s insides. Here's what the `createEndpoint` facade looks like:

```
    public static function createEndpoint($baseUrl, array $definitions, array $listeners = array(), HttpAdapterInterface $adapter = null, Router $router = null)
    {
        if (!$adapter) {
            $adapter = new GuzzleHttpAdapter(new Client());
        }

        if (!$router) {
            $router = new Router();
        }

        if (!count($listeners)) {
            $serializerAdapter = new SymfonySerializerAdapter(
                new Serializer([], [new JsonEncoder()])
            );
            $serializerAdapter->addContentType('application/json', 'json');

            $listeners[Endpoint::LISTENER_AFTER_RESPONSE] = new ContentTypeListenener($serializerAdapter);
            $listeners[Endpoint::LISTENER_AFTER_DATA] = new ReflectionHydratorListener();
        }

        $endpoint = new Endpoint($adapter, $router, $baseUrl);

        foreach ($listeners as $evt => $listener) {
            $endpoint->addListener($evt, $listener);
        }

        $driver = new AnnotationDriver();

        foreach ($definitions as $interfaceName) {
            $resources = $driver->createDefinitions($interfaceName);
            foreach ($resources as $resource) {
                $endpoint->addResourceDefinition($resource);
            }
        }

        return $endpoint;
    }
```

Note that you still can pass an array of listeners, an HTTP adapter and a router as arguments to this factory method. However, if you want to do things manually, or you dislike when things are decided for you (or, you just hate static facades), you could always reimplement this logic manually. That's all there is, basically.

Now, to the specifics. Why do we need all these things the `Builder` instantiates for us?

HTTP layer
----------

[](#http-layer)

First of all, in order to consume an API, you need to somehow interact with the 3rd party server. For this, you need an HTTP adapter. It should implement `Kix\Apiranha\HttpAdapter\HttpAdapterInterface`, and `GuzzleHttpAdapter` is an example you can use right now.

The only method you have to implement is `send(RequestInterface $request): ResponseInterface`, where `RequestInterface` and `ResponseInterface` are standard `Psr\Http` messages.

Resource definitions
--------------------

[](#resource-definitions)

When processing your annotated interface you've created before, the endpoint registers it as a resource. A resource is an instance of `Kix\Apiranha\ResourceDefinitionInterface`, which basically contains all the data necessary to make an HTTP request to an API. You might think of it like a Swagger definition.

Router
------

[](#router)

A router is responsible for generating concrete URIs when given a resource and an array of parameters the request is executed with. The built-in router allows you to pass extra parameters you have not explicitly declared in a resource's path. Those will be added as query parameters.

Listeners
---------

[](#listeners)

You can attach your own or built-in listeners to the lifecycle, which consists of three events:

- `Endpoint::LISTENER_BEFORE_REQUEST`, used to modify the request before it has been sent,
- `Endpoint::LISTENER_AFTER_RESPONSE`, which is used to attach serializers to the workflow,
- `Endpoint::LISTENER_AFTER_DATA`, which is used to hydrate your model instances.

All of the listeners can be implemented as callables, and 'after response' and 'after data' listeners can also implement `Kix\Apiranha\Listener\AfterResponseListenerInterface` or `Kix\Apiranha\Listener\AfterDataListenerInterface`.

### BEFORE\_REQUEST listeners

[](#before_request-listeners)

A listener that is attached to the `Endpoint::LISTENER_BEFORE_REQUEST` receives just one argument: the `ResponseInterface`instance that has been instantiated for the current request. For example, you could use it to pass authorization headers to a secured API:

```
$endpoint = Builder::createEndpoint('http://api.github.com', [GithubApi::class]);

$endpoint->addListener(Endpoint::LISTENER_BEFORE_REQUEST, function (RequestInterface $request) {
    return $request->withAddedHeader('Authorization', 'Basic 12345');
});
```

### AFTER\_RESPONSE listeners

[](#after_response-listeners)

A listener attached to `Endpoint::LISTENER_AFTER_RESPONSE` could either be a callable that matches the signature of `function(RequestInterface $request, ResponseInterface $response)` or an instance of `Kix\Apiranha\Listener\AfterResponseListenerInterface`. For example, such a listener could be helpful in case when the HTTP client you are using does not throw for bad HTTP status codes.

Here's `StatusCodeListener`, for example:

```
class StatusCodeListener implements AfterResponseListenerInterface
{
    /**
     * @param RequestInterface  $request
     * @param ResponseInterface $response
     * @throws \Exception
     * @return void
     */
    public function process(RequestInterface $request, ResponseInterface $response)
    {
        if ($response->getStatusCode() > 400) {
            throw new \RuntimeException(sprintf(
                'Bad status code: %s',
                $response->getStatusCode()
            ));
        }
    }
}
```

Having the request also available allows you to log precise request/response interactions, for example.

Also note that you're allowed to return an instance of `ApiResponse`, which wraps the PSR response, but also has a `data`property which you can manipulate.

### AFTER\_DATA listeners

[](#after_data-listeners)

A listener attached to `Endpoint::LISTENER_AFTER_DATA` could either be a callable that matches the signature of `function(ResponseInterface $response, ResourceDefinitionInterface $resource)` or an instance of `Kix\Apiranha\Listener\AfterDataListenerInterface`.

Such listeners are used by the library to hydrate objects with data returned by the API we wrap.

#### Hydration

[](#hydration)

There are several options available for the hydration strategies:

- Hydration via reflection, implemented in `ReflectionHydratorListener`
- Hydration with `ocramius/generated-hydrator`, implemented in `GeneratedHydratorListener`

Reflection hydrator is always available, as long as PHP has reflection APIs available. Generated hydrator is an alternative implementation which could be more performant.

Both of these strategies are implemented as listeners and can be attached to the `Endpoint::LISTENER_AFTER_DATA` event:

```
$endpoint = new Endpoint($adapter, new Router(), 'http://api.github.com');
$endpoint->addListener(Endpoint::LISTENER_AFTER_DATA, new ReflectionHydratorListener());
```

**TODO**: benchmarks

Once the model instance has been hydrated, you get back an instance of the class you've declared as your model. And that's it!

###  Health Score

20

—

LowBetter than 14% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/685f6393a3f357d69fbbc886e2ea3bb1031076c4b0f206ca81e290af6ad5d2f5?d=identicon)[kix](/maintainers/kix)

---

Top Contributors

[![kix](https://avatars.githubusercontent.com/u/345754?v=4)](https://github.com/kix "kix (48 commits)")

### Embed Badge

![Health badge](/badges/kix-apiranha/health.svg)

```
[![Health](https://phpackages.com/badges/kix-apiranha/health.svg)](https://phpackages.com/packages/kix-apiranha)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
