nshelper

WP Global Cart - ECB Currency Converter; Retrieves Euro foreign exchan

Sep 16th, 2026 (edited)
69
0
Never
2
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 7.97 KB | None | 0 0
  1. <?php
  2.  
  3.     /**
  4.      * WP Global Cart - ECB Currency Converter
  5.      *
  6.      * Retrieves Euro foreign exchange reference rates from the ECB
  7.      * and converts amounts between currencies using EUR as the base.
  8.      */
  9.     class WOOGC_Currency_Converter {
  10.  
  11.         /**
  12.          * ECB daily XML feed.
  13.          */
  14.         const ECB_FEED_URL = 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml';
  15.  
  16.         /**
  17.          * WordPress option used to store the rates.
  18.          */
  19.         const OPTION_RATES = 'woogc_ecb_currency_rates';
  20.  
  21.         /**
  22.          * WordPress option used to store the date of the last
  23.          * successfully downloaded ECB rates.
  24.          */
  25.         const OPTION_DATE = 'woogc_ecb_currency_rates_date';
  26.  
  27.         /**
  28.          * WordPress option used to store the last update timestamp.
  29.          */
  30.         const OPTION_UPDATED = 'woogc_ecb_currency_rates_updated';
  31.  
  32.         /**
  33.          * Convert an amount from one currency to another.
  34.          *
  35.          * Example:
  36.          * 100 EUR -> USD
  37.          * 100 RON -> USD
  38.          *
  39.          * @param float  $amount
  40.          * @param string $from_currency
  41.          * @param string $to_currency
  42.          *
  43.          * @return float|false
  44.          */
  45.         public static function convert( $amount, $from_currency, $to_currency ) {
  46.  
  47.             $amount = (float) $amount;
  48.  
  49.             $from_currency = strtoupper( trim( $from_currency ) );
  50.             $to_currency   = strtoupper( trim( $to_currency ) );
  51.  
  52.             // Nothing to convert.
  53.             if ( $from_currency === $to_currency ) {
  54.                 return $amount;
  55.             }
  56.  
  57.             // Make sure we have current rates, or the latest available rates.
  58.             $rates = self::get_rates();
  59.  
  60.             if ( ! is_array( $rates ) || empty( $rates ) ) {
  61.                 return false;
  62.             }
  63.  
  64.             /*
  65.              * EUR is the base currency.
  66.              *
  67.              * Example:
  68.              * EUR = 1
  69.              * USD = 1.1551
  70.              * RON = 5.2568
  71.              */
  72.             $from_rate = 1.0;
  73.             $to_rate   = 1.0;
  74.  
  75.             /*
  76.              * Currency other than EUR must exist in the ECB rates.
  77.              */
  78.             if ( 'EUR' !== $from_currency ) {
  79.  
  80.                 if ( ! isset( $rates[ $from_currency ] ) ) {
  81.                     return false;
  82.                 }
  83.  
  84.                 $from_rate = (float) $rates[ $from_currency ];
  85.             }
  86.  
  87.             if ( 'EUR' !== $to_currency ) {
  88.  
  89.                 if ( ! isset( $rates[ $to_currency ] ) ) {
  90.                     return false;
  91.                 }
  92.  
  93.                 $to_rate = (float) $rates[ $to_currency ];
  94.             }
  95.  
  96.             /*
  97.              * Convert:
  98.              *
  99.              * source currency -> EUR -> target currency
  100.              *
  101.              * Example RON -> USD:
  102.              *
  103.              * 100 / 5.2568 * 1.1551
  104.              */
  105.             return ( $amount / $from_rate ) * $to_rate;
  106.         }
  107.  
  108.         /**
  109.          * Get the currently stored ECB rates.
  110.          *
  111.          * This will attempt to update the rates once per day.
  112.          * If today's update is unavailable, the previously stored
  113.          * successful rates are returned.
  114.          *
  115.          * @return array|false
  116.          */
  117.         public static function get_rates() {
  118.  
  119.             $stored_rates = get_option( self::OPTION_RATES, array() );
  120.             $stored_date  = get_option( self::OPTION_DATE, '' );
  121.  
  122.             /*
  123.              * If we already have rates downloaded today, use them.
  124.              */
  125.             $today = current_time( 'Y-m-d' );
  126.  
  127.             if (
  128.                 ! empty( $stored_rates ) &&
  129.                 $stored_date === $today
  130.             ) {
  131.                 return $stored_rates;
  132.             }
  133.  
  134.             /*
  135.              * Try to download fresh ECB rates.
  136.              */
  137.             $new_rates = self::update_rates();
  138.  
  139.             /*
  140.              * Successful update.
  141.              */
  142.             if ( is_array( $new_rates ) && ! empty( $new_rates ) ) {
  143.                 return $new_rates;
  144.             }
  145.  
  146.             /*
  147.              * ECB unavailable / no new rates today.
  148.              *
  149.              * Fall back to the last successful rates.
  150.              */
  151.             if ( ! empty( $stored_rates ) ) {
  152.                 return $stored_rates;
  153.             }
  154.  
  155.             return false;
  156.         }
  157.  
  158.         /**
  159.          * Download and store the latest ECB rates.
  160.          *
  161.          * @return array|false
  162.          */
  163.         public static function update_rates() {
  164.  
  165.             $response = wp_remote_get(
  166.                 self::ECB_FEED_URL,
  167.                 array(
  168.                     'timeout'     => 15,
  169.                     'redirection' => 3,
  170.                     'sslverify'   => true,
  171.                     'headers'     => array(
  172.                         'Accept' => 'application/xml,text/xml',
  173.                     ),
  174.                 )
  175.             );
  176.  
  177.             if ( is_wp_error( $response ) ) {
  178.                 return false;
  179.             }
  180.  
  181.             $status_code = wp_remote_retrieve_response_code( $response );
  182.  
  183.             if ( 200 !== $status_code ) {
  184.                 return false;
  185.             }
  186.  
  187.             $body = wp_remote_retrieve_body( $response );
  188.  
  189.             if ( empty( $body ) ) {
  190.                 return false;
  191.             }
  192.  
  193.             /*
  194.              * Prevent XML external entity processing.
  195.              */
  196.             libxml_use_internal_errors( true );
  197.  
  198.             $xml = simplexml_load_string(
  199.                 $body,
  200.                 'SimpleXMLElement',
  201.                 LIBXML_NONET | LIBXML_NOCDATA
  202.             );
  203.  
  204.             if ( false === $xml ) {
  205.                 libxml_clear_errors();
  206.                 return false;
  207.             }
  208.  
  209.             libxml_clear_errors();
  210.  
  211.             $rates = array(
  212.                 'EUR' => 1.0,
  213.             );
  214.  
  215.             /*
  216.              * ECB XML structure:
  217.              *
  218.              * <Cube currency="USD" rate="1.1551"/>
  219.              */
  220.             $currency_nodes = $xml->xpath(
  221.                 '//*[local-name()="Cube"][@currency and @rate]'
  222.             );
  223.  
  224.             if ( empty( $currency_nodes ) ) {
  225.                 return false;
  226.             }
  227.  
  228.             foreach ( $currency_nodes as $node ) {
  229.  
  230.                 $currency = strtoupper( (string) $node['currency'] );
  231.                 $rate     = (float) $node['rate'];
  232.  
  233.                 if ( empty( $currency ) || $rate <= 0 ) {
  234.                     continue;
  235.                 }
  236.  
  237.                 $rates[ $currency ] = $rate;
  238.             }
  239.  
  240.             /*
  241.              * Make sure we got a reasonable set of rates.
  242.              */
  243.             if ( count( $rates ) < 2 ) {
  244.                 return false;
  245.             }
  246.  
  247.             /*
  248.              * Save only after successfully parsing the complete feed.
  249.              *
  250.              * This is important: a failed/invalid request must never
  251.              * overwrite the last known-good rates.
  252.              */
  253.             $today = current_time( 'Y-m-d' );
  254.  
  255.             update_option(
  256.                 self::OPTION_RATES,
  257.                 $rates,
  258.                 false
  259.             );
  260.  
  261.             update_option(
  262.                 self::OPTION_DATE,
  263.                 $today,
  264.                 false
  265.             );
  266.  
  267.             update_option(
  268.                 self::OPTION_UPDATED,
  269.                 time(),
  270.                 false
  271.             );
  272.  
  273.             return $rates;
  274.         }
  275.  
  276.         /**
  277.          * Get information about the currently stored rates.
  278.          *
  279.          * Useful for debugging/admin UI.
  280.          *
  281.          * @return array
  282.          */
  283.         public static function get_status() {
  284.  
  285.             return array(
  286.                 'date'    => get_option( self::OPTION_DATE, '' ),
  287.                 'updated' => get_option( self::OPTION_UPDATED, 0 ),
  288.                 'rates'   => get_option( self::OPTION_RATES, array() ),
  289.             );
  290.         }
  291.     }
Advertisement
Comments
  • User was banned
  • User was banned
Add Comment
Please, Sign In to add comment