PHP code example of peterlupu / curs-bnr

1. Go to this page and download the library: Download peterlupu/curs-bnr library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

peterlupu / curs-bnr example snippets


use CursBnr\CursBnr;

$bnr = new CursBnr();

// The latest communicated rate
$rate = $bnr->latest('EUR');
$rate->value;                 // 5.246  (RON per 1 EUR)
$rate->communicatedOnYmd();   // '2026-08-04'

// The rate legally valid on a given date (this is the one you invoice with!)
$rate = $bnr->validOn('EUR', new DateTimeImmutable('2024-01-27'));
$rate->value;                 // 4.9763 — communicated Friday Jan 26

// Convert between any two currencies
$factor = $bnr->pair('EUR', 'RON', new DateTimeImmutable('2024-01-27'));
$amountInRon = $amountInEur * $factor;

$rate = $bnr->validOn('HUF', new DateTimeImmutable('2024-01-27'));

$rate->currency;              // 'HUF'
$rate->value;                 // 0.013620 — RON per 1 HUF (multiplier already applied!)
$rate->multiplier;            // 100 — BNR quotes HUF per 100 units; we normalize for you
$rate->communicatedAt;        // DateTimeImmutable of the communication date
$rate->validFrom();           // communicatedAt + 1 day, per art. 290

$bnr->pair('EUR', 'RON');                             // 4.9763       — the rate valid TODAY
$bnr->pair('RON', 'EUR');                             // 0.20095...   — full precision, no rounding
$bnr->pair('EUR', 'USD');                             // cross rate via RON
$bnr->pair('EUR', 'EUR');                             // 1.0 — no HTTP request at all
$bnr->pair('EUR', 'RON', new DateTimeImmutable('2024-01-27')); // valid-on-date semantics

$day = $bnr->latestDay();          // one HTTP call

$day->rate('EUR')->value;
$day->rate('USD')->value;
$day->rate('CHF')->value;
$day->communicatedAt;              // when this Cube was communicated
$day->validFrom();                 // the day these rates become valid
$day->has('MDL');                  // true
foreach ($day->rates as $code => $rate) { /* all ~30 of them */ }

$bnr->validDayOn(new DateTimeImmutable('2024-01-27'));        // rates valid on that day
$bnr->communicatedDayOn(new DateTimeImmutable('2024-01-26')); // rates communicated that exact day

use CursBnr\Exception\CursBnrException;

try {
    $rate = $bnr->validOn('EUR', $date);
} catch (CursBnrException $e) {
    // network down, feed broken, unknown currency, ... — one catch to rule them all
}

use CursBnr\CursBnr;
use CursBnr\FeedUrls;
use CursBnr\Http\RetryPolicy;

$bnr = new CursBnr(
    retryPolicy: new RetryPolicy(attempts: 5, backoffMs: [100, 500, 2000]),
    maxWalkBackDays: 30,
);

new RetryPolicy(attempts: 4, backoffMs: fn (int $retry) => $retry * 250);
RetryPolicy::none(); // fail fast

$bnr = new CursBnr(clock: fn () => new DateTimeImmutable('2024-01-29'));

use CursBnr\Http\HttpClientInterface;

final class MyClient implements HttpClientInterface
{
    public function get(string $url): string { /* ... */ }
}

$bnr = new CursBnr(httpClient: new MyClient());

use CursBnr\Http\Psr18HttpClient;

$bnr = new CursBnr(httpClient: new Psr18HttpClient(
    new \GuzzleHttp\Client(),
    new \GuzzleHttp\Psr7\HttpFactory(),
));

$bnr = new CursBnr(urls: new FeedUrls(
    daily: 'https://my-mirror.example/nbrfxrates.xml',
    yearlyTemplate: 'https://my-mirror.example/years/nbrfxrates{year}.xml',
));

$key = "bnr:EUR:RON:{$date->format('Y-m-d')}";

$value = $cache->get($key);

if ($value === null) {
    $value = $bnr->pair('EUR', 'RON', $date);
    $cache->set($key, $value, 600);
}

use CursBnr\CursBnr;
use CursBnr\ExchangeRates;

final class InvoiceTotals
{
    public function __construct(private readonly ExchangeRates $bnr) {}

    public function inRon(float $amountEur, \DateTimeInterface $issuedOn): float
    {
        return $amountEur * $this->bnr->pair('EUR', 'RON', $issuedOn);
    }
}

// production wiring: bind ExchangeRates => new CursBnr()

use CursBnr\Testing\FakeCursBnr;

$bnr = FakeCursBnr::constant(['EUR' => 5.0, 'USD' => 4.5]);

$totals = new InvoiceTotals($bnr);
$totals->inRon(100.0, new DateTimeImmutable('2024-01-27'));  // 500.0 — always

$bnr = (new FakeCursBnr())
    ->communicate('2024-01-25', ['EUR' => 4.9765])
    ->communicate('2024-01-26', ['EUR' => 4.9763]);
    // nothing on 27-28 — the weekend

$bnr->validOn('EUR', new DateTimeImmutable('2024-01-26'))->value;  // 4.9765 — Thursday's (strictly before!)
$bnr->validOn('EUR', new DateTimeImmutable('2024-01-27'))->value;  // 4.9763 — Friday's
$bnr->latest('EUR')->value;                                        // 4.9763 — most recent registration

$bnr->communicatedOn('EUR', new DateTimeImmutable('2024-01-27')); // NoRateCommunicatedException (weekend)
$bnr->latest('CHF');                                              // UnknownCurrencyException (not registered)
FakeCursBnr::constant(['EUR' => 5.0], maxWalkBackDays: 3);        // bound configurable, like the real thing

// pin "today" to make the future-date guard deterministic in tests
new FakeCursBnr(today: '2024-01-29');

$service = new InvoiceTotals(FakeCursBnr::constant(['EUR' => 5.0]));

// app/Providers/AppServiceProvider.php
public function register(): void
{
    $this->app->singleton(\CursBnr\ExchangeRates::class, \CursBnr\CursBnr::class);
}

// in a feature test — every resolved service now sees the fake
$this->app->instance(
    \CursBnr\ExchangeRates::class,
    \CursBnr\Testing\FakeCursBnr::constant(['EUR' => 5.0]),
);

// in a KernelTestCase / WebTestCase
static::getContainer()->set(
    \CursBnr\ExchangeRates::class,
    \CursBnr\Testing\FakeCursBnr::constant(['EUR' => 5.0]),
);

// PHP-DI example
return [
    CursBnr\ExchangeRates::class => DI\autowire(CursBnr\CursBnr::class),
];