Fixer is a simple and lightweight API for foreign exchange rates that supports up to 170 world currencies. | |
currencylayer provides reliable exchange rates and currency conversions for your business up to 168 world currencies. | |
exchangerates provides reliable exchange rates and currency conversions for your business with over 15 data sources. |
- Installation
- Configuration
- Usage
- Cache
- Rates Caching
- Query Cache Options
- Requests Caching
- Creating a Service
- Supported Services
Swap is decoupled from any library sending HTTP requests (like Guzzle), instead it uses an abstraction called HTTPlug 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. Below is an example using the curl client:
composer require php-http/curl-client nyholm/psr7 php-http/message florianv/swap
Before starting to retrieve currency exchange rates, we need to build Swap
. Fortunately, the Builder
class helps us to perform this task.
Let's say we want to use the Fixer service and fallback to currencylayer in case of failure. We would write the following:
use Swap\Builder;
$swap = (new Builder())
->add('apilayer_fixer', ['api_key' => 'Get your key here: https://fixer.io/'])
->add('apilayer_currency_data', ['api_key' => 'Get your key here: https://currencylayer.com'])
->add('apilayer_exchange_rates_data', ['api_key' => 'Get your key here: https://exchangeratesapi.io/'])
->build();
As you can see, you can use the add()
method to add a service. You can add as many as you want, they will be called in a chain, in case of failure.
We recommend to use one of the services that support our project, providing a free plan up to 100 requests per month.
The complete list of all supported services is available here.
In order to get rates, you can use the latest()
or historical()
methods on Swap
:
// Latest rate
$rate = $swap->latest('EUR/USD');
// 1.129
echo $rate->getValue();
// 2016-08-26
echo $rate->getDate()->format('Y-m-d');
// Historical rate
$rate = $swap->historical('EUR/USD', (new \DateTime())->modify('-15 days'));
Currencies are expressed as their ISO 4217 code.
When using the chain service, it can be useful to know which service provided the rate.
You can use the getProviderName()
function on a rate that gives you the name of the service that returned it:
$name = $rate->getProviderName();
For example, if Fixer returned the rate, it will be identical to fixer
.
Exchanger
provides a PSR-16 Simple Cache integration allowing you to cache rates during a given time using the adapter of your choice.
The following example uses the Predis
cache from php-cache.com PSR-6 implementation installable using composer require cache/predis-adapter
.
You will also need to install a "bridge" that allows to adapt the PSR-6 adapters to PSR-16 using composer require cache/simple-cache-bridge
(https://github.com/php-cache/simple-cache-bridge).
$ composer require cache/predis-adapter
use Cache\Adapter\Predis\PredisCachePool;
use Cache\Bridge\SimpleCache\SimpleCacheBridge;
$client = new \Predis\Client('tcp:/127.0.0.1:6379');
$psr6pool = new PredisCachePool($client);
$simpleCache = new SimpleCacheBridge($psr6pool);
$swap = (new Builder(['cache_ttl' => 3600, 'cache_key_prefix' => 'myapp-']))
->useSimpleCache($simpleCache)
->build();
All rates will now be cached in Redis during 3600 seconds, and cache keys will be prefixed with 'myapp-'
You can override Swap
caching options per request.
Set cache TTL in seconds. Default: null
- cache entries permanently
// Override the global cache_ttl only for this query
$rate = $swap->latest('EUR/USD', ['cache_ttl' => 60]);
$rate = $swap->historical('EUR/USD', $date, ['cache_ttl' => 60]);
Disable/Enable caching. Default: true
// Disable caching for this query
$rate = $swap->latest('EUR/USD', ['cache' => false]);
$rate = $swap->historical('EUR/USD', $date, ['cache' => false]);
Set the cache key prefix. Default: empty string
There is a limitation of 64 characters for the key length in PSR-6, because of this, key prefix must not exceed 24 characters, as sha1() hash takes 40 symbols.
PSR-6 do not allows characters {}()/\@:
in key, these characters are replaced with -
// Override cache key prefix for this query
$rate = $swap->latest('EUR/USD', ['cache_key_prefix' => 'currencies-special-']);
$rate = $swap->historical('EUR/USD', $date, ['cache_key_prefix' => 'currencies-special-']);
By default, Swap
queries the service for each rate you request, but some services like Fixer
sends a whole file containing
rates for each base currency.
It means that if you are requesting multiple rates using the same base currency like EUR/USD
, then EUR/GBP
, you may want
to cache these responses in order to improve performances.
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:
use Http\Client\Common\PluginClient;
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\Builder;
$pool = new ArrayCachePool();
$streamFactory = new GuzzleStreamFactory();
$cachePlugin = new CachePlugin($pool, $streamFactory);
$client = new PluginClient(new GuzzleClient(), [$cachePlugin]);
$swap = (new Builder())
->useHttpClient($client)
->add('apilayer_fixer', ['api_key' => 'Get your key here: https://fixer.io/'])
->add('apilayer_currency_data', ['api_key' => 'Get your key here: https://currencylayer.com'])
->add('apilayer_exchange_rates_data', ['api_key' => 'Get your key here: https://exchangeratesapi.io/'])
->build();
// A http request is sent
$rate = $swap->latest('EUR/USD');
// A new request won't be sent
$rate = $swap->latest('EUR/GBP');
You want to add a new service to Swap
? Great!
If your service must send http requests to retrieve rates, your class must extend the HttpService
class, otherwise you can extend the more generic Service
class.
In the following example, we are creating a Constant
service that returns a constant rate value.
use Exchanger\Contract\ExchangeRateQuery;
use Exchanger\Contract\ExchangeRate;
use Exchanger\Service\HttpService;
use Swap\Service\Registry;
class ConstantService extends HttpService
{
/**
* Gets the exchange rate.
*
* @param ExchangeRateQuery $exchangeQuery
*
* @return ExchangeRate
*/
public function getExchangeRate(ExchangeRateQuery $exchangeQuery): ExchangeRate
{
// If you want to make a request you can use
// $content = $this->request('http://example.com');
return $this->createInstantRate($exchangeQuery->getCurrencyPair(), $this->options['value']);
}
/**
* Processes the service options.
*
* @param array &$options
*
* @return void
*/
public function processOptions(array &$options): void
{
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): bool
{
// For example, our service only supports EUR as base currency
return 'EUR' === $exchangeQuery->getCurrencyPair()->getBaseCurrency();
}
/**
* Gets the name of the exchange rate service.
*
* @return string
*/
public function getName(): string
{
return 'constant';
}
}
// Register the service so it's available using Builder::add()
Registry::register('constant', ConstantService::class);
// Now you can use the add() method add your service name and pass your options
$swap = (new Builder())
->add('constant', ['value' => 10])
->build();
// 10
echo $swap->latest('EUR/USD')->getValue();
If your service supports retrieving historical rates, you need to use the SupportsHistoricalQueries
trait.
You will need to rename the getExchangeRate
method to getLatestExchangeRate
and switch its visibility to protected, and implement a new getHistoricalExchangeRate
method:
use Exchanger\Service\SupportsHistoricalQueries;
class ConstantService extends HttpService
{
use SupportsHistoricalQueries;
/**
* Gets the exchange rate.
*
* @param ExchangeRateQuery $exchangeQuery
*
* @return ExchangeRate
*/
protected function getLatestExchangeRate(ExchangeRateQuery $exchangeQuery): ExchangeRate
{
return $this->createInstantRate($exchangeQuery->getCurrencyPair(), $this->options['value']);
}
/**
* Gets an historical rate.
*
* @param HistoricalExchangeRateQuery $exchangeQuery
*
* @return ExchangeRate
*/
protected function getHistoricalExchangeRate(HistoricalExchangeRateQuery $exchangeQuery): ExchangeRate
{
return $this->createInstantRate($exchangeQuery->getCurrencyPair(), $this->options['value']);
}
}
Here is the complete list of supported services and their possible configurations:
use Swap\Builder;
$swap = (new Builder())
->add('apilayer_fixer', ['api_key' => 'Get your key here: https://fixer.io/'])
->add('apilayer_currency_data', ['api_key' => 'Get your key here: https://currencylayer.com'])
->add('apilayer_exchange_rates_data', ['api_key' => 'Get your key here: https://exchangeratesapi.io/'])
->add('abstract_api', ['api_key' => 'Get your key here: https://app.abstractapi.com/users/signup'])
->add('coin_layer', ['access_key' => 'secret', 'paid' => false])
->add('fixer', ['access_key' => 'your-access-key'])
->add('currency_layer', ['access_key' => 'secret', 'enterprise' => false])
->add('exchange_rates_api', ['access_key' => 'secret'])
->add('european_central_bank')
->add('national_bank_of_romania')
->add('central_bank_of_republic_turkey')
->add('central_bank_of_czech_republic')
->add('russian_central_bank')
->add('bulgarian_national_bank')
->add('webservicex')
->add('forge', ['api_key' => 'secret'])
->add('cryptonator')
->add('currency_data_feed', ['api_key' => 'secret'])
->add('currency_converter', ['access_key' => 'secret', 'enterprise' => false])
->add('open_exchange_rates', ['app_id' => 'secret', 'enterprise' => false])
->add('xignite', ['token' => 'token'])
->add('xchangeapi', ['api-key' => 'secret'])
->add('array', [
[
'EUR/USD' => 1.1,
'EUR/GBP' => 1.5
],
[
'2017-01-01' => [
'EUR/USD' => 1.5
],
'2017-01-03' => [
'EUR/GBP' => 1.3
],
]
])
->build();