Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- /**
- * WP Global Cart - ECB Currency Converter
- *
- * Retrieves Euro foreign exchange reference rates from the ECB
- * and converts amounts between currencies using EUR as the base.
- */
- class WOOGC_Currency_Converter {
- /**
- * ECB daily XML feed.
- */
- const ECB_FEED_URL = 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml';
- /**
- * WordPress option used to store the rates.
- */
- const OPTION_RATES = 'woogc_ecb_currency_rates';
- /**
- * WordPress option used to store the date of the last
- * successfully downloaded ECB rates.
- */
- const OPTION_DATE = 'woogc_ecb_currency_rates_date';
- /**
- * WordPress option used to store the last update timestamp.
- */
- const OPTION_UPDATED = 'woogc_ecb_currency_rates_updated';
- /**
- * Convert an amount from one currency to another.
- *
- * Example:
- * 100 EUR -> USD
- * 100 RON -> USD
- *
- * @param float $amount
- * @param string $from_currency
- * @param string $to_currency
- *
- * @return float|false
- */
- public static function convert( $amount, $from_currency, $to_currency ) {
- $amount = (float) $amount;
- $from_currency = strtoupper( trim( $from_currency ) );
- $to_currency = strtoupper( trim( $to_currency ) );
- // Nothing to convert.
- if ( $from_currency === $to_currency ) {
- return $amount;
- }
- // Make sure we have current rates, or the latest available rates.
- $rates = self::get_rates();
- if ( ! is_array( $rates ) || empty( $rates ) ) {
- return false;
- }
- /*
- * EUR is the base currency.
- *
- * Example:
- * EUR = 1
- * USD = 1.1551
- * RON = 5.2568
- */
- $from_rate = 1.0;
- $to_rate = 1.0;
- /*
- * Currency other than EUR must exist in the ECB rates.
- */
- if ( 'EUR' !== $from_currency ) {
- if ( ! isset( $rates[ $from_currency ] ) ) {
- return false;
- }
- $from_rate = (float) $rates[ $from_currency ];
- }
- if ( 'EUR' !== $to_currency ) {
- if ( ! isset( $rates[ $to_currency ] ) ) {
- return false;
- }
- $to_rate = (float) $rates[ $to_currency ];
- }
- /*
- * Convert:
- *
- * source currency -> EUR -> target currency
- *
- * Example RON -> USD:
- *
- * 100 / 5.2568 * 1.1551
- */
- return ( $amount / $from_rate ) * $to_rate;
- }
- /**
- * Get the currently stored ECB rates.
- *
- * This will attempt to update the rates once per day.
- * If today's update is unavailable, the previously stored
- * successful rates are returned.
- *
- * @return array|false
- */
- public static function get_rates() {
- $stored_rates = get_option( self::OPTION_RATES, array() );
- $stored_date = get_option( self::OPTION_DATE, '' );
- /*
- * If we already have rates downloaded today, use them.
- */
- $today = current_time( 'Y-m-d' );
- if (
- ! empty( $stored_rates ) &&
- $stored_date === $today
- ) {
- return $stored_rates;
- }
- /*
- * Try to download fresh ECB rates.
- */
- $new_rates = self::update_rates();
- /*
- * Successful update.
- */
- if ( is_array( $new_rates ) && ! empty( $new_rates ) ) {
- return $new_rates;
- }
- /*
- * ECB unavailable / no new rates today.
- *
- * Fall back to the last successful rates.
- */
- if ( ! empty( $stored_rates ) ) {
- return $stored_rates;
- }
- return false;
- }
- /**
- * Download and store the latest ECB rates.
- *
- * @return array|false
- */
- public static function update_rates() {
- $response = wp_remote_get(
- self::ECB_FEED_URL,
- array(
- 'timeout' => 15,
- 'redirection' => 3,
- 'sslverify' => true,
- 'headers' => array(
- 'Accept' => 'application/xml,text/xml',
- ),
- )
- );
- if ( is_wp_error( $response ) ) {
- return false;
- }
- $status_code = wp_remote_retrieve_response_code( $response );
- if ( 200 !== $status_code ) {
- return false;
- }
- $body = wp_remote_retrieve_body( $response );
- if ( empty( $body ) ) {
- return false;
- }
- /*
- * Prevent XML external entity processing.
- */
- libxml_use_internal_errors( true );
- $xml = simplexml_load_string(
- $body,
- 'SimpleXMLElement',
- LIBXML_NONET | LIBXML_NOCDATA
- );
- if ( false === $xml ) {
- libxml_clear_errors();
- return false;
- }
- libxml_clear_errors();
- $rates = array(
- 'EUR' => 1.0,
- );
- /*
- * ECB XML structure:
- *
- * <Cube currency="USD" rate="1.1551"/>
- */
- $currency_nodes = $xml->xpath(
- '//*[local-name()="Cube"][@currency and @rate]'
- );
- if ( empty( $currency_nodes ) ) {
- return false;
- }
- foreach ( $currency_nodes as $node ) {
- $currency = strtoupper( (string) $node['currency'] );
- $rate = (float) $node['rate'];
- if ( empty( $currency ) || $rate <= 0 ) {
- continue;
- }
- $rates[ $currency ] = $rate;
- }
- /*
- * Make sure we got a reasonable set of rates.
- */
- if ( count( $rates ) < 2 ) {
- return false;
- }
- /*
- * Save only after successfully parsing the complete feed.
- *
- * This is important: a failed/invalid request must never
- * overwrite the last known-good rates.
- */
- $today = current_time( 'Y-m-d' );
- update_option(
- self::OPTION_RATES,
- $rates,
- false
- );
- update_option(
- self::OPTION_DATE,
- $today,
- false
- );
- update_option(
- self::OPTION_UPDATED,
- time(),
- false
- );
- return $rates;
- }
- /**
- * Get information about the currently stored rates.
- *
- * Useful for debugging/admin UI.
- *
- * @return array
- */
- public static function get_status() {
- return array(
- 'date' => get_option( self::OPTION_DATE, '' ),
- 'updated' => get_option( self::OPTION_UPDATED, 0 ),
- 'rates' => get_option( self::OPTION_RATES, array() ),
- );
- }
- }
Advertisement