exchange rate pairs. * * @param string ecb_rates The XML received from ECB. * @retur array */ protected function decode_rates($ecb_rates) { $exchange_rates = []; foreach($ecb_rates->Cube->Cube->Cube as $rate) { $exchange_rates[(string)$rate['currency']] = (float)$rate['rate']; } // ECB feed is based against EUR, but it doesn't contain such currency. We // can safely add it manually, with an exchange rate of 1 $exchange_rates['EUR'] = 1; return $exchange_rates; } /** * Fetches all exchange rates from ECB API. * * @return object|bool An object containing the response from Open Exchange, or * False in case of failure. */ private function fetch_all_rates() { try { $response = \Httpful\Request::get($this->ecb_api_rates_url) ->expectsXml() ->send(); if($response->hasErrors()) { // OpenExchangeRates sends error details in response body if($response->hasBody()) { $response_data = $response->body; $this->add_error(self::ERR_ERROR_RETURNED, sprintf(__('Error returned by ECB. Error code: %s. Error message: %s - %s.', 'woocommerce-aelia-currencyswitcher'), $response_data->status, $response_data->message, $response_data->description)); } return false; } return $response->body; } catch(Exception $e) { $this->add_error(self::ERR_EXCEPTION_OCCURRED, sprintf(__('Exception occurred while retrieving the exchange rates from ECB. Error message: %s.', 'woocommerce-aelia-currencyswitcher'), $e->getMessage())); return null; } } /** * Returns current exchange rates for the specified currency. * * @param string base_currency The base currency. * @return array An array of Currency => Exchange Rate pairs. */ private function current_rates($base_currency) { if(empty($this->_current_rates) || $this->_base_currency != $base_currency) { $cache_key = md5(get_class($this)) . $base_currency; // Try to get the cached rates for the specified base currency, if any $this->_current_rates = $this->get_cached_exchange_rates($cache_key); if(!empty($this->_current_rates)) { return $this->_current_rates; } // Fetch exchange rates $ecb_exchange_rates = $this->fetch_all_rates(); if($ecb_exchange_rates === false) { return null; } // ECB rates are returned as JSON representation of an array of objects. // We need to transform it into an array of currency => rate pairs $exchange_rates = $this->decode_rates($ecb_exchange_rates); if(!is_array($exchange_rates)) { $this->add_error(self::ERR_UNEXPECTED_ERROR_FETCHING_EXCHANGE_RATES, __('An unexpected error occurred while fetching exchange rates ' . 'from ECB. The most common cause of this issue is the ' . 'absence of PHP CURL extension. Please make sure that ' . 'PHP CURL is installed and configured in your system.', 'woocommerce-aelia-currencyswitcher')); return array(); } // Since we didn't get the exchange rates related to the base currency, // but in the default base currency used by OpenExchange, we need to // recalculate them against the base currency we would like to use $this->_current_rates = $this->rebase_rates($exchange_rates, $base_currency); $this->_base_currency = $base_currency; // Cache the exchange rates $this->cache_exchange_rates($cache_key, $this->_current_rates); } return $this->_current_rates; } /** * Recaculates the exchange rates using another base currency. This method * is invoked because the rates fetched from ECB are relative to BitCoin, * but another currency is most likely is used by WooCommerce. * * @param array exchange_rates The exchange rates retrieved from ECB. * @param string base_currency The base currency against which the rates should be recalculated. * @return array An array of currency => exchange rate pairs. */ private function rebase_rates(array $exchange_rates, $base_currency) { $recalc_rate = $exchange_rates[$base_currency] ?? null; if(empty($recalc_rate)) { $this->add_error(self::ERR_BASE_CURRENCY_NOT_FOUND, sprintf(__('Could not rebase rates against base currency "%s". Currency not found in data returned by ECB.', 'woocommerce-aelia-currencyswitcher'), $base_currency)); return null; } $result = array(); foreach($exchange_rates as $currency => $rate) { $result[$currency] = $rate / $recalc_rate; } return $result; } /** * Returns the exchange rate of a currency in respect to a base currency. * * @param string base_currency The code of the base currency. * @param string currency The code of the currency for which to find the exchange rate. * @return float */ protected function get_rate($base_currency, $currency) { $current_rates = $this->current_rates($base_currency); return get_value($currency, $current_rates); } /** * Class constructor. * * @param array An array of Settings that can be used to override the ones * currently saved in the configuration. * @return Exchange_Rates_ECB_Model. */ public function __construct($settings = null) { parent::__construct($settings); } } /** * Add the ECB model to the list of exchange rates providers available to the Currency Switcher. * * @param array * @return array */ add_filter('wc_aelia_cs_exchange_rates_models', function($exchange_rates_models): array { // Pass the ECB Model to the Currency Switcher $exchange_rates_models['ecb'] = (object)array( 'class_name' => 'Aelia_CS_Exchange_Rates_ECB_Model', 'label' => 'ECB - Custom', ); return $exchange_rates_models; }); }