├── LICENSE ├── README.md ├── composer.json ├── composer.lock └── src ├── Currency.php ├── ECB.php ├── ECBConverter.php └── ECBException.php /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2021 Richard Boldiš 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ECB-API 2 | European Central Bank EURO exchange PHP API 3 | 4 | ## Installation with Composer 5 | ```bash 6 | composer require richardds/ecb-api 7 | ``` 8 | 9 | ## Examples 10 | 11 | ### EURO to FOREIGN, FOREIGN to EURO 12 | 13 | ```php 14 | require __DIR__ . '/vendor/autoload.php'; 15 | 16 | use Richardds\ECBAPI\ECBConverter; 17 | 18 | $converter = new ECBConverter(); 19 | 20 | echo $converter->toEuro(150, 'USD', 3); 21 | echo $converter->toForeign(150, 'USD'); 22 | print_r($converter->toForeign(150, ['EUR', 'USD', 'CHF', 'RUB', 'CZK'])) . PHP_EOL; 23 | ``` 24 | ```text 25 | 126.968 26 | 177.21 27 | Array 28 | ( 29 | [EUR] => 150 30 | [USD] => 126.96800406298 31 | [CHF] => 130.68478829064 32 | [RUB] => 2.1159184778929 33 | [CZK] => 5.736137667304 34 | ) 35 | ``` 36 | 37 | ### Exchange rate list 38 | 39 | ```php 40 | require __DIR__ . '/vendor/autoload.php'; 41 | 42 | use Richardds\ECBAPI\ECBConverter; 43 | 44 | $converter = new ECBConverter(); 45 | 46 | $references = $converter->list(true); 47 | 48 | foreach ($references as $code => $rate) { 49 | if ($code === 'EUR') { 50 | continue; 51 | } 52 | 53 | printf("1.00 EUR = %.2f %s\n1.00 %s = %.2f EUR\n", $rate, $code, $code, (1 / $rate)); 54 | } 55 | ``` 56 | ```text 57 | 1.00 EUR = 1.18 USD 58 | 1.00 USD = 0.85 EUR 59 | 1.00 EUR = 130.31 JPY 60 | 1.00 JPY = 0.01 EUR 61 | 1.00 EUR = 1.96 BGN 62 | 1.00 BGN = 0.51 EUR 63 | 1.00 EUR = 26.15 CZK 64 | 1.00 CZK = 0.04 EUR 65 | ... 66 | ``` 67 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "richardds/ecb-api", 3 | "description": "European Central Bank EURO exchange PHP API", 4 | "minimum-stability": "stable", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Richard Boldiš", 9 | "email": "richard@boldis.dev" 10 | } 11 | ], 12 | "autoload": { 13 | "psr-4": { 14 | "Richardds\\ECBAPI\\": "src/" 15 | } 16 | }, 17 | "require": { 18 | "php": ">=7.1|^8.0", 19 | "ext-curl": "*", 20 | "ext-simplexml": "*" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "c4fb71348416236f75b45f121dfcc01c", 8 | "packages": [], 9 | "packages-dev": [], 10 | "aliases": [], 11 | "minimum-stability": "stable", 12 | "stability-flags": [], 13 | "prefer-stable": false, 14 | "prefer-lowest": false, 15 | "platform": { 16 | "php": ">=7.1|^8.0", 17 | "ext-curl": "*", 18 | "ext-simplexml": "*" 19 | }, 20 | "platform-dev": [], 21 | "plugin-api-version": "2.1.0" 22 | } 23 | -------------------------------------------------------------------------------- /src/Currency.php: -------------------------------------------------------------------------------- 1 | 'Euro', 16 | 'USD' => 'United States dollar', 17 | 'JPY' => 'Japanese yen', 18 | 'BGN' => 'Bulgarian lev', 19 | 'CZK' => 'Czech koruna', 20 | 'DKK' => 'Danish krone', 21 | 'GBP' => 'Pound sterling', 22 | 'HUF' => 'Hungarian forint', 23 | 'PLN' => 'Polish złoty', 24 | 'RON' => 'Romanian leu', 25 | 'SEK' => 'Swedish krona', 26 | 'CHF' => 'Swiss franc', 27 | 'ISK' => 'Icelandic króna', 28 | 'NOK' => 'Norwegian krone', 29 | 'TRY' => 'Turkish lira', 30 | 'AUD' => 'Australian dollar', 31 | 'BRL' => 'Brazilian real', 32 | 'CAD' => 'Canadian dollar', 33 | 'CNY' => 'Renminbi', 34 | 'HKD' => 'Hong Kong dollar', 35 | 'IDR' => 'Indonesian rupiah', 36 | 'ILS' => 'Israeli new shekel', 37 | 'INR' => 'Indian rupee', 38 | 'KRW' => 'South Korean won', 39 | 'MXN' => 'Mexican peso', 40 | 'MYR' => 'Malaysian ringgit', 41 | 'NZD' => 'New Zealand dollar', 42 | 'PHP' => 'Philippine peso', 43 | 'SGD' => 'Singapore dollar', 44 | 'THB' => 'Thai baht', 45 | 'ZAR' => 'South African rand' 46 | ]; 47 | 48 | /** 49 | * @var string 50 | */ 51 | private $code; 52 | 53 | /** 54 | * @var float 55 | */ 56 | private $rate; 57 | 58 | public function __construct(string $code, float $rate) 59 | { 60 | $this->code = $code; 61 | $this->rate = $rate; 62 | } 63 | 64 | public function getName(): string 65 | { 66 | return self::CURRENCY_NAME_TABLE[$this->code] ?? ''; 67 | } 68 | 69 | public function getCode(): string 70 | { 71 | return $this->code; 72 | } 73 | 74 | public function getRate(): float 75 | { 76 | return $this->rate; 77 | } 78 | 79 | public function setRate(float $rate): void 80 | { 81 | $this->rate = $rate; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/ECB.php: -------------------------------------------------------------------------------- 1 | Cube->Cube->Cube as $row) { 31 | $code = (string)($row['currency'] ?? ''); 32 | $rate = (double)($row['rate'] ?? 0); 33 | 34 | if (empty($code) || strlen($code) !== 3) { 35 | throw new ECBException('Currency code is invalid', ECBException::DATA_PARSE_FAILED); 36 | } 37 | 38 | if ($rate <= 0) { 39 | throw new ECBException('Currency rate is invalid', ECBException::DATA_PARSE_FAILED); 40 | } 41 | 42 | $exchange_references[$code] = new Currency($code, $rate); 43 | } 44 | 45 | return $exchange_references; 46 | } 47 | 48 | throw new ECBException('', ECBException::DATA_PARSE_FAILED); 49 | } 50 | 51 | /** 52 | * @param string $url 53 | * @return string 54 | * @throws ECBException 55 | */ 56 | private static function fetch(string $url): string 57 | { 58 | $ch = curl_init($url . '?' . uniqid('', true)); 59 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 60 | 61 | if (($data = @curl_exec($ch)) !== false) { 62 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 63 | 64 | if ($http_code != 200) { 65 | curl_close($ch); 66 | throw new ECBException('HTTP_CODE != 200', ECBException::DATA_DOWNLOAD_FAILED); 67 | } 68 | 69 | curl_close($ch); 70 | 71 | return $data; 72 | } 73 | 74 | $curl_error = curl_error($ch); 75 | curl_close($ch); 76 | 77 | throw new ECBException($curl_error, ECBException::DATA_DOWNLOAD_FAILED); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/ECBConverter.php: -------------------------------------------------------------------------------- 1 | cache_file = $cache_file; 35 | $this->cache_timeout = $cache_timeout; 36 | } 37 | 38 | /** 39 | * @throws ECBException 40 | */ 41 | public function checkFileCache(): void 42 | { 43 | if (!file_exists($this->cache_file) || time() - filemtime($this->cache_file) > $this->cache_timeout) { 44 | $this->reloadExchangeReferences(); 45 | file_put_contents($this->cache_file, serialize($this->exchange_data), LOCK_EX); 46 | } elseif (is_null($this->exchange_data)) { 47 | $this->exchange_data = unserialize(file_get_contents($this->cache_file)); 48 | } 49 | } 50 | 51 | /** 52 | * Converts foreign currency to euro. 53 | * 54 | * @param int $amount 55 | * @param string|array $currency_code 56 | * @param int|null $round 57 | * @return int|array 58 | * @throws ECBException 59 | */ 60 | public function toEuro(int $amount, $currency_code, ?int $round = null) 61 | { 62 | return $this->convert($amount, $currency_code, function ($amount, $rate) use ($round) { 63 | $val = $amount / $rate; 64 | return !is_null($round) ? round($val, $round) : $val; 65 | }); 66 | } 67 | 68 | /** 69 | * @throws ECBException 70 | */ 71 | private function check(): void 72 | { 73 | if (!empty($this->cache_file)) { 74 | $this->checkFileCache(); 75 | } elseif (is_null($this->exchange_data)) { 76 | $this->reloadExchangeReferences(); 77 | } 78 | } 79 | 80 | /** 81 | * @param bool $asArray 82 | * @return Currency[]|array 83 | * @throws ECBException 84 | */ 85 | public function list(bool $asArray = false): array 86 | { 87 | $this->check(); 88 | 89 | $references = $this->exchange_data; 90 | 91 | if ($asArray) { 92 | $array = []; 93 | 94 | foreach ($references as $reference) { 95 | $array[$reference->getCode()] = $reference->getRate(); 96 | } 97 | 98 | return $array; 99 | } 100 | 101 | return $references; 102 | } 103 | 104 | /** 105 | * @param int $amount 106 | * @param string|array $currency_code 107 | * @param callable $callback 108 | * @return int|array 109 | * @throws ECBException 110 | */ 111 | public function convert(float $amount, $currency_code, callable $callback) 112 | { 113 | $this->check(); 114 | 115 | // Selected currencies 116 | if (is_array($currency_code)) { 117 | $results = []; 118 | 119 | foreach ($currency_code as $currency_c) { 120 | $results[$currency_c] = $callback($amount, $this->exchange_data[$currency_c]->getRate()); 121 | } 122 | 123 | return $results; 124 | } 125 | 126 | // All currencies 127 | if ($currency_code === '*') { 128 | $results = []; 129 | 130 | foreach ($this->exchange_data as $currency) { 131 | $results[$currency->getCode()] = $callback($amount, $currency->getRate()); 132 | } 133 | 134 | return $results; 135 | } 136 | 137 | // Single currency 138 | return $callback($amount, $this->exchange_data[$currency_code]->getRate()); 139 | } 140 | 141 | /** 142 | * Reloads ECB exchange references. 143 | * 144 | * @throws ECBException 145 | */ 146 | public function reloadExchangeReferences(): void 147 | { 148 | try { 149 | $this->exchange_data = ECB::getExchangeReferences(); 150 | } catch (ECBException $e) { 151 | throw new ECBException('Failed to update ESB exchange references', 152 | ECBException::CONVERT_FAILED, $e); 153 | } 154 | } 155 | 156 | /** 157 | * Converts euro to foreign currency. 158 | * 159 | * @param int $amount 160 | * @param string|array $currency_code 161 | * @param int|null $precision 162 | * @return int|array 163 | * @throws ECBException 164 | */ 165 | public function toForeign(float $amount, string $currency_code, ?int $precision = null) 166 | { 167 | return $this->convert($amount, $currency_code, function ($amount, $rate) use ($precision) { 168 | $val = $amount * $rate; 169 | return !is_null($precision) ? round($val, $precision) : $val; 170 | }); 171 | } 172 | 173 | public function getCacheFile(): string 174 | { 175 | return $this->cache_file; 176 | } 177 | 178 | public function setCacheFile(string $cache_file): void 179 | { 180 | $this->cache_file = $cache_file; 181 | } 182 | 183 | public function getCacheTimeout(): int 184 | { 185 | return $this->cache_timeout; 186 | } 187 | 188 | public function setCacheTimeout(int $cache_timeout): void 189 | { 190 | $this->cache_timeout = $cache_timeout; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/ECBException.php: -------------------------------------------------------------------------------- 1 | code}]: {$this->message}\n"; 60 | } 61 | } 62 | --------------------------------------------------------------------------------