Skip to content

Commit

Permalink
Implemented exchanger
Browse files Browse the repository at this point in the history
  • Loading branch information
florianv committed Sep 8, 2016
1 parent 2b2a26b commit abf00c6
Show file tree
Hide file tree
Showing 108 changed files with 3,954 additions and 1,928 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ before_script:
- travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-dist

script:
- bin/phpunit --coverage-text
- composer test
Empty file modified LICENSE
100644 → 100755
Empty file.
57 changes: 28 additions & 29 deletions README.md
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,44 +1,43 @@
<img src="doc/logo.png" width="200px" align="left"/>
> The exchange rates library for PHP
# Exchanger

[![Build status](http://img.shields.io/travis/florianv/swap.svg?style=flat-square)](https://travis-ci.org/florianv/swap)
[![Total Downloads](https://img.shields.io/packagist/dt/florianv/swap.svg?style=flat-square)](https://packagist.org/packages/florianv/swap)
[![Scrutinizer](https://img.shields.io/scrutinizer/g/florianv/swap.svg?style=flat-square)](https://scrutinizer-ci.com/g/florianv/swap)
[![Version](http://img.shields.io/packagist/v/florianv/swap.svg?style=flat-square)](https://packagist.org/packages/florianv/swap)
> Currency exchange rates framework for PHP
Swap allows you to retrieve currency exchange rates from various providers such as Google or Yahoo and optionally cache the results.
It is integrated to other libraries such as [`moneyphp/money`](https://github.com/moneyphp/money) and provides
a [Symfony Bundle](https://github.com/florianv/FlorianvSwapBundle) and a [Laravel Package](https://github.com/florianv/laravel-swap).
[![Build status](http://img.shields.io/travis/florianv/exchanger/master.svg?style=flat-square)](https://travis-ci.org/florianv/exchanger)
[![Total Downloads](https://img.shields.io/packagist/dt/florianv/exchanger.svg?style=flat-square)](https://packagist.org/packages/florianv/exchanger)
[![Scrutinizer](https://img.shields.io/scrutinizer/g/florianv/exchanger.svg?style=flat-square)](https://scrutinizer-ci.com/g/florianv/exchanger)
[![Version](http://img.shields.io/packagist/v/florianv/exchanger.svg?style=flat-square)](https://packagist.org/packages/florianv/exchanger)

## Documentation
`Exchanger` is a PHP framework to work with currency exchange rates.

**Looking for a simple library based on `Exchanger` ? Check out [Swap](https://github.com/florianv/swap) !**

The documentation can be found [here](https://github.com/florianv/swap/blob/master/doc/doc.md).
## Documentation

## Providers
The documentation can be found [here](https://github.com/florianv/exchanger/blob/master/doc/doc.md).

| Provider | Base Currency | Quote Currency |
|---------------------------------------------------------------------------|----------------------|----------------|
| [European Central Bank](http://www.ecb.europa.eu/home/html/index.en.html) | EUR | * |
| [Google Finance](http://www.google.com/finance) | * | * |
| [Open Exchange Rates](https://openexchangerates.org) | USD (free), * (paid) | * |
| [Xignite](https://www.xignite.com) | * | * |
| [Yahoo Finance](https://finance.yahoo.com) | * | * |
| [WebserviceX](http://www.webservicex.net/ws/default.aspx) | * | * |
| [National Bank of Romania](http://www.bnr.ro) | RON | * |
| [Central Bank of the Republic of Turkey](http://www.tcmb.gov.tr) | * | TRY |
| [Central Bank of the Czech Republic](http://www.cnb.cz) | * | CZK |
| [currencylayer](https://currencylayer.com) | USD (free), * (paid) | * |
## Services

## Integrations
Here is the list of the currently implemented services.

- A Symfony Bundle [FlorianvSwapBundle](https://github.com/florianv/FlorianvSwapBundle)
- A Laravel Package [florianv/laravel-swap](https://github.com/florianv/laravel-swap)
| Service | Base Currency | Quote Currency | Historical |
|---------------------------------------------------------------------------|----------------------|----------------|----------------|
| [Fixer](http://fixer.io) | * | * | Yes |
| [European Central Bank](http://www.ecb.europa.eu/home/html/index.en.html) | EUR | * | Yes |
| [Google Finance](http://www.google.com/finance) | * | * | No |
| [Open Exchange Rates](https://openexchangerates.org) | USD (free), * (paid) | * | Yes |
| [Xignite](https://www.xignite.com) | * | * | Yes |
| [Yahoo Finance](https://finance.yahoo.com) | * | * | No |
| [WebserviceX](http://www.webservicex.net/ws/default.aspx) | * | * | No |
| [National Bank of Romania](http://www.bnr.ro) | RON | * | No |
| [Central Bank of the Republic of Turkey](http://www.tcmb.gov.tr) | * | TRY | No |
| [Central Bank of the Czech Republic](http://www.cnb.cz) | * | CZK | No |
| [currencylayer](https://currencylayer.com) | USD (free), * (paid) | * | Yes |

## Credits

- [Florian Voutzinos](https://github.com/florianv)
- [All Contributors](https://github.com/florianv/swap/contributors)
- [All Contributors](https://github.com/florianv/exchanger/contributors)

## License

The MIT License (MIT). Please see [LICENSE](https://github.com/florianv/swap/blob/master/LICENSE) for more information.
The MIT License (MIT). Please see [LICENSE](https://github.com/florianv/exchanger/blob/master/LICENSE) for more information.
17 changes: 7 additions & 10 deletions composer.json
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "florianv/swap",
"name": "florianv/exchanger",
"type": "library",
"description": "Exchange rates library for PHP",
"description": "Currency exchange rates framework for PHP",
"keywords": ["currency", "money", "rate", "conversion", "exchange rates"],
"homepage": "https://github.com/florianv/swap",
"homepage": "https://github.com/florianv/exchanger",
"license": "MIT",
"authors": [
{
Expand All @@ -14,12 +14,12 @@
],
"autoload": {
"psr-4": {
"Swap\\": "src/"
"Exchanger\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Swap\\": "tests/"
"Exchanger\\": "tests/"
}
},
"require": {
Expand All @@ -35,15 +35,12 @@
"php-http/message": "^1.0",
"psr/cache": "^1.0"
},
"config": {
"bin-dir": "bin"
},
"scripts": {
"test": "phpunit"
"test": "vendor/bin/phpunit"
},
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
"dev-master": "0.1-dev"
}
}
}
197 changes: 153 additions & 44 deletions doc/doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,95 +2,135 @@

## Installation

Swap is decoupled from any library sending HTTP requests (like Guzzle), instead it uses an abstraction called [HTTPlug](http://httplug.io/) which provides the http layer used to send requests to exchange rate services. This gives you the flexibility to choose what HTTP client and PSR-7 implementation you want to use.
`Exchanger` is decoupled from any library sending HTTP requests (like Guzzle), instead it uses an abstraction called [HTTPlug](http://httplug.io/) which provides the http layer used to send requests to exchange rate services. This gives you the flexibility to choose what HTTP client and PSR-7 implementation you want to use.

Read more about the benefits of this and about what different HTTP clients you may use in the [HTTPlug documentation](http://docs.php-http.org/en/latest/httplug/users.html). Below is an example using Guzzle6:

```bash
composer require florianv/swap php-http/message php-http/guzzle6-adapter
composer require florianv/exchanger php-http/message php-http/guzzle6-adapter
```

## Usage

First, you need to create a **service** and add it to `Exchanger`.

> You can consult the list of supported services [here](https://github.com/florianv/exchanger/blob/master/README.md#services).
```php
use Http\Adapter\Guzzle6\Client as GuzzleClient;
use Swap\Provider\YahooFinanceProvider;
use Swap\Swap;
use Exchanger\Service\Fixer;
use Exchanger\Exchanger;

// Instantiate your Http Adapter
$httpAdapter = new GuzzleClient();
// Create your http client (we choose Guzzle 6 here)
$client = new GuzzleClient();

// Create the Yahoo Finance provider
$yahooProvider = new YahooFinanceProvider($httpAdapter);
// Create the Fixer.io service
$service = new Fixer($client);

// Create Swap with the provider
$swap = new Swap($yahooProvider);
// Create Exchanger with the Fixer.io service
$exchanger = new Exchanger($service);
```

### Quoting
### Querying

To retrieve the latest exchange rate for a currency pair, you need to use the `quote()` method.
#### Latest rates

`Exchanger` uses a concept of queries. In order to get an exchange rate, you need to build a **query** and `Exchanger` will process it to return the rate. The example below shows how to get the **latest** `EUR/USD` exchange rate.

```php
$rate = $swap->quote('EUR/USD');
use Exchanger\ExchangeRateQueryBuilder;

// Create the query to get the latest EUR/USD rate
$query = (new ExchangeRateQueryBuilder('EUR/USD'))
->build();

// 1.187220
echo $rate;
// Get the exchange rate
$rate = $exchanger->getExchangeRate($query);

// 1.187220
// 1.1159
echo $rate->getValue();

// 15-01-11 21:30:00
echo $rate->getDate()->format('Y-m-d H:i:s');
// 2016-09-06
echo $rate->getDate()->format('Y-m-d');
```

> Currencies are expressed as their [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) code.
### Chaining providers
#### Historical rates

`Exchanger` allows you to retrieve **historical** exchange rates but not all services support this feature as you can see in this [table](https://github.com/florianv/exchanger/blob/master/README.md#services).

```php
// Create the query to get the EUR/USD rate 15 days ago
$query = (new ExchangeRateQueryBuilder('EUR/USD'))
->setDate((new \DateTime())->modify('-15 days'))
->build();

// Get the exchange rate
$rate = $exchanger->getExchangeRate($query);

// 1.1339
echo $rate->getValue();

// 2016-08-23
echo $rate->getDate()->format('Y-m-d');
```

### Chaining services

It is possible to chain providers in order to use fallbacks in case the main providers don't support the currency or are unavailable.
Simply create a `ChainProvider` wrapping the providers you want to chain.
It is possible to chain services in order to use fallbacks in case the previous ones don't support the currency or are unavailable.
Simply create a `Chain` service to wrap the services you want to chain.

```php
use Swap\Provider\ChainProvider;
use Swap\Provider\YahooFinanceProvider;
use Exchanger\Service\Chain;
use Exchanger\Service\Yahoo;

$chainProvider = new ChainProvider([
new YahooFinanceProvider($httpAdapter),
new GoogleFinanceProvider($httpAdapter)
$service = new Chain([
new Fixer($client),
new Yahoo($client)
]);
```

The rates will be first fetched using the Yahoo Finance provider and will fallback to Google Finance.
The rates will be first fetched using the `Fixer` service and will fallback to `Yahoo`.

### Caching

#### Rates Caching

Swap provides a [PSR-6 Caching Interface](http://www.php-fig.org/psr/psr-6) integration allowing you to cache rates during a given time using the adapter of your choice.

#### Example
`Exchanger` provides a [PSR-6 Caching Interface](http://www.php-fig.org/psr/psr-6) integration allowing you to cache rates during a given time using the adapter of your choice.

The following example uses the Apcu cache from [php-cache.com](http://php-cache.com) PSR-6 implementation installable using `composer require cache/apcu-adapter`.

```php
use Cache\Adapter\Apcu\ApcuCachePool;
use Swap\Swap;

$cachePool = new ApcuCachePool();
$swap = new Swap($yahooProvider, $cachePool, 3600);
$exchanger = new Exchanger($service, new ApcuCachePool(), ['cache_ttl' => 3600]);
```

All rates will now be cached in Apcu during 3600 seconds.

##### Query Cache Control

For more control, you can configure the cache per query.

```php
// Override the global cache_ttl only for this query
$query = (new ExchangeRateQueryBuilder('JPY/GBP'))
->addOption('cache_ttl', 60)
->build();

// Force refreshing the rate from the service for this query
$query = (new ExchangeRateQueryBuilder('JPY/GBP'))
->addOption('cache_refresh', true)
->build();
```

#### Requests Caching

By default, Swap queries the provider for each rate you request, but some providers like the `EuropeanCentralBankProvider`
return the same response no matter the requested currency pair. It means performances can be improved when using these providers
By default, `Exchanger` queries the service for each rate you request, but some services like the `EuropeanCentralBank`
return the same response no matter the requested currency pair. It means performances can be improved when using these services
and when quoting multiple pairs during the same request.

#### Example

Install the PHP HTTP Cache plugin and the PHP Cache Array adapter `composer require php-http/cache-plugin cache/array-adapter`.

Modify the way you create your HTTP Client by decorating it with a `PluginClient` using the `Array` cache:
Expand All @@ -101,21 +141,90 @@ use Http\Client\Common\Plugin\CachePlugin;
use Http\Message\StreamFactory\GuzzleStreamFactory;
use Http\Adapter\Guzzle6\Client as GuzzleClient;
use Cache\Adapter\PHPArray\ArrayCachePool;
use Swap\Provider\EuropeanCentralBankProvider;
use Swap\Swap;
use Exchanger\Service\EuropeanCentralBank;
use Exchanger\ExchangeRateQueryBuilder;
use Exchanger\Exchanger;

$pool = new ArrayCachePool();
$streamFactory = new GuzzleStreamFactory();
$cachePlugin = new CachePlugin($pool, $streamFactory);
$httpAdapter = new PluginClient(new GuzzleClient(), [$cachePlugin]);

$yahooProvider = new EuropeanCentralBankProvider($httpAdapter);
$service = new EuropeanCentralBank($httpAdapter);

$exchanger = new Exchanger($service);

$swap = new Swap($yahooProvider);
$query = (new ExchangeRateQueryBuilder('EUR/USD'))->build();

// A http request is sent
$rate = $swap->quote('EUR/USD');
// An http request will be sent
$rate = $exchanger->getExchangeRate((new ExchangeRateQueryBuilder('EUR/USD'))->build());

// A new request won't be sent
$rate = $swap->quote('EUR/GBP');
$rate = $exchanger->getExchangeRate((new ExchangeRateQueryBuilder('EUR/GBP'))->build());
```

### Creating a Service

First you must check if the service supports retrieval of historical rates. If it's the case, you must extend the `HistoricalService` class,
otherwise use the `Service` class.

In the following example, we are creating a `Constant` service that returns a constant rate value.

```php
use Exchanger\Service\Service;
use Exchanger\Contract\ExchangeRateQuery;
use Exchanger\ExchangeRate;

class ConstantService extends Service
{
/**
* Gets the exchange rate.
*
* @param ExchangeRateQuery $exchangeQuery
*
* @return ExchangeRate
*/
public function getExchangeRate(ExchangeRateQuery $exchangeQuery)
{
// If you want to make a request you can use
$content = $this->request('http://example.com');

return new ExchangeRate($this->options['value']);
}

/**
* Processes the service options.
*
* @param array &$options
*
* @return array
*/
public function processOptions(array &$options)
{
if (!isset($options['value'])) {
throw new \InvalidArgumentException('The "value" option must be provided.');
}
}

/**
* Tells if the service supports the exchange rate query.
*
* @param ExchangeRateQuery $exchangeQuery
*
* @return bool
*/
public function supportQuery(ExchangeRateQuery $exchangeQuery)
{
// For example, our service only supports EUR as base currency
return 'EUR' === $exchangeQuery->getCurrencyPair()->getBaseCurrency();
}
}

$service = new ConstantService(null, null, ['value' => 10]);
$exchanger = new Exchanger($service);

$query = (new ExchangeRateQueryBuilder('EUR/USD'))->build();

// 10
$rate = $exchanger->getExchangeRate($query)->getValue();
```
Binary file removed doc/logo.png
Binary file not shown.

0 comments on commit abf00c6

Please sign in to comment.