lemonade/component_currency

Typed PHP currency conversion library using Czech National Bank exchange rates, filesystem cache, fallback values, and a lightweight hexagonal architecture.

Maintainers

Package info

github.com/johnnyxlemonade/component_currency

pkg:composer/lemonade/component_currency

Statistics

Installs: 21

Dependents: 0

Suggesters: 0

Stars: 2

Open Issues: 0

v1.3.0 2026-05-18 10:43 UTC

This package is auto-updated.

Last update: 2026-05-18 10:48:14 UTC


README

PHP Version Packagist Version Downloads PHPStan Tests License

Lemonade Currency Conversion Library

Lemonade Currency Conversion Library provides a general-purpose currency converter using the Czech National Bank (CNB) as the primary data source.

The library exposes a simple developer-friendly API while internally using a layered, hexagonal-style architecture. This keeps the public API stable and easy to use, while infrastructure concerns such as CNB fetching, parsing, caching, and time resolution stay isolated behind explicit ports and adapters.

Features

  • Supports PHP 8.1+
  • Fully typed and optimized for static analysis with PHPStan Level 10, strict rules, and bleeding edge
  • Provides exchange rates and currency values for supported currencies
  • Uses the Czech National Bank (CNB) as the primary data source
  • Caches CNB yearly exchange-rate data in the filesystem
  • Falls back to default values when CNB data is unavailable
  • Supports nearest previous available rate lookup
  • Automatically adjusts to CNB's publishing schedule:
    • Exchange rates are published on working days after 14:30.
    • Before 14:30, the previous day's rates are used as the current rate.
  • Provides a backward-compatible facade API for simple usage

Supported Data Sources

  • Czech National Bank (CNB)

Supported Currencies

  • CZK - Czech Republic
  • EUR - Eurozone
  • HUF - Hungary
  • PLN - Poland
  • GBP - Great Britain
  • USD - United States

Installation

Use Composer to install the library:

composer require lemonade/component_currency

Architecture

The library is organized using a lightweight hexagonal architecture.

src/
  Domain/
    CurrencyCode.php
    CurrencyCatalog.php
    DefaultCurrencyRates.php
    ExchangeRateTable.php

  Application/
    CurrencyRateService.php
    CurrencyRateServiceFactory.php

  Port/
    Clock.php
    ExchangeRateCache.php
    ExchangeRateSource.php

  Infrastructure/
    Cache/
      FileExchangeRateCache.php
    Clock/
      SystemClock.php
    Cnb/
      CnbExchangeRateSource.php
      CnbRateParser.php
      LineSplitter.php
      RegexLineSplitter.php

  CurrencyRate.php
  CurrencyMarket.php
  CurrencyList.php
  CurrencyStorage.php
  CurrencyData.php

Domain

The domain layer contains the core currency model and rules:

  • supported currency codes
  • default currency values
  • currency metadata such as symbols and names
  • parsed exchange-rate table representation

It does not depend on CNB, filesystem, HTTP, or runtime environment.

Application

The application layer contains the main use case:

  • resolving the requested date
  • applying CNB publishing-time rules
  • reading rates from cache
  • fetching rates from the configured source
  • parsing CNB data
  • falling back to default values when needed

The primary service is:

Lemonade\Currency\Application\CurrencyRateService

Ports

Ports define contracts for external concerns:

Lemonade\Currency\Port\ExchangeRateSource
Lemonade\Currency\Port\ExchangeRateCache
Lemonade\Currency\Port\Clock

This allows custom implementations for fetching rates, caching data, or controlling time in tests.

Infrastructure

Infrastructure contains concrete adapters:

  • CnbExchangeRateSource fetches CNB data
  • CnbRateParser parses CNB text data
  • FileExchangeRateCache stores CNB data in the filesystem
  • SystemClock provides current runtime time
  • RegexLineSplitter splits CNB text data into lines

Facade API

The public facade classes remain available for simple usage and backward compatibility:

Lemonade\Currency\CurrencyRate
Lemonade\Currency\CurrencyMarket
Lemonade\Currency\CurrencyList

Usage

Basic static API

use DateTimeImmutable;
use Lemonade\Currency\CurrencyRate;

// Get the ratio of a foreign currency against the local currency.
$eurRatio = CurrencyRate::getRatio(currency: 'EUR');

// Get the value of a foreign currency against CZK.
$usdValue = CurrencyRate::getValue(currency: 'USD');

// Resolve a rate for a specific date.
$eurRatioForDate = CurrencyRate::getRatio(
    currency: 'EUR',
    date: new DateTimeImmutable('2023-12-01')
);

$usdValueForDate = CurrencyRate::getValue(
    currency: 'USD',
    date: new DateTimeImmutable('2023-12-01')
);

Shortcut methods

use DateTimeImmutable;
use Lemonade\Currency\CurrencyRate;

$eur = CurrencyRate::eur();
$usd = CurrencyRate::usd();
$gbp = CurrencyRate::gbp();
$pln = CurrencyRate::pln();
$huf = CurrencyRate::huf();

$eurForDate = CurrencyRate::eur(new DateTimeImmutable('2023-12-01'));

Market object API

use DateTimeImmutable;
use Lemonade\Currency\CurrencyMarket;

$market = new CurrencyMarket(new DateTimeImmutable('2023-12-01'));

$eurRatio = $market->getRatio(currency: 'EUR');
$usdValue = $market->getValue(currency: 'USD');

Currency metadata

use Lemonade\Currency\CurrencyList;

$currencies = CurrencyList::getCurrencies();

$symbols = CurrencyList::getCurrencySymbolList();

$eurSymbol = CurrencyList::getCurrencySymbol('EUR');

$eurName = CurrencyList::getCurrencyLanguageName('EUR');

Advanced Usage

Custom storage directory

By default, the library stores CNB cache files in:

storage/export/cnb

You can create the application service manually with a custom storage directory:

use Lemonade\Currency\Application\CurrencyRateServiceFactory;

$service = CurrencyRateServiceFactory::createDefault(
    storageDirectory: __DIR__ . '/var/cache/cnb'
);

$value = $service->getValue('EUR');

Custom cache, source, parser, or clock

For full control, instantiate the application service directly:

use Lemonade\Currency\Application\CurrencyRateService;
use Lemonade\Currency\Domain\DefaultCurrencyRates;
use Lemonade\Currency\Infrastructure\Cache\FileExchangeRateCache;
use Lemonade\Currency\Infrastructure\Clock\SystemClock;
use Lemonade\Currency\Infrastructure\Cnb\CnbExchangeRateSource;
use Lemonade\Currency\Infrastructure\Cnb\CnbRateParser;

$service = new CurrencyRateService(
    cache: new FileExchangeRateCache(__DIR__ . '/var/cache/cnb'),
    source: new CnbExchangeRateSource(),
    parser: new CnbRateParser(),
    defaults: new DefaultCurrencyRates(),
    clock: new SystemClock(),
);

$value = $service->getValue('EUR');

This is useful when integrating the library into applications that already have their own cache layer, HTTP transport, or deterministic clock.

Custom exchange-rate source

Implement the ExchangeRateSource port to fetch data from a different source.

use Lemonade\Currency\Port\ExchangeRateSource;

final class CustomExchangeRateSource implements ExchangeRateSource
{
    public function fetchYear(int $year): string
    {
        // Return raw yearly exchange-rate data in the format expected by the configured parser.
        return 'Datum|1 EUR' . PHP_EOL . '2.1.2024|24,725';
    }

    public function getUrl(int $year): string
    {
        return 'custom://currency-rates/' . $year;
    }
}

CNB Data Notes

CNB yearly exchange-rate data uses multipliers in the header, for example:

Datum|1 EUR|100 HUF|1 PLN
2.1.2024|24,725|6,505|5,688

The parser normalizes values to a single currency unit. For example, 100 HUF with value 6,505 is stored as 0.06505 per 1 HUF.

Configuration

The default filesystem cache directory is:

storage/export/cnb

Ensure this directory is writable by the PHP process when using the default service factory.

Testing

Run unit tests:

vendor/bin/phpunit

Run static analysis:

vendor/bin/phpstan analyse

Quality

The library is designed for:

  • strict typing
  • PHPStan Level 10
  • strict rules
  • bleeding-edge static analysis
  • isolated domain logic
  • testable infrastructure adapters
  • stable backward-compatible facade API

Contributing

Feel free to submit issues or create pull requests to improve this library.

License

This library is licensed under the MIT License. See the LICENSE file for details.