elliotwms/Abacus

View on GitHub
src/Currency.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

namespace Abacus;

/**
 * Class Currency
 * @package Abacus
 */
class Currency
{
    /**
     * @var string
     */
    public $name = 'US Dollar';         // Common singular currency name

    /**
     * @var string
     */
    public $name_plural = 'US Dollars'; // Common plural currency name

    /**
     * @var string
     */
    public $code = 'USD';               // ISO code

    /**
     * @var string
     */
    public $symbol = '$';               // Standard currency symbol ($/£/€)

    /**
     * @var string
     */
    public $symbol_native = '$';        // Locally used currency symbol

    /**
     * @var float
     */
    public $rate = 1;                   // The currency's rate to USD

    /**
     * @var string
     */
    public $thousands_separator = ',';  // Thousands separator for formatting

    /**
     * @var string
     */
    public $decimal_separator = '.';    // Decimal separator for formatting

    /**
     * @var int
     */
    public $decimal_digits = 2;         // How many decimal digits to display

    /**
     * @var int
     */
    public $rounding = 0;               // Currency rounding method
    public $updated_at;                 // When the currency was last updated

    /**
     * __construct
     *
     * Create a new Currency object
     *
     * @param string $currency The currency's ISO code
     *
     * @throws AbacusException
     */
    public function __construct($currency = "USD")
    {
        // Get the Currency object from the exchange.json file
        $currency = $this->_getCurrency($currency);

        // Get the attributes from the currency JSON object
        $this->_mapCurrency($currency);
    }

    /**
     * Get Currency
     *
     * Gets a currency from the exchange.json
     *
     * @param $currency
     * @return mixed
     * @throws AbacusException
     */
    private function _getCurrency($currency)
    {
        if (!file_exists(static::_getExchangePath())) {
            throw new AbacusException("Exchange rates not found. Please poll");
        }

        $exchange = json_decode(file_get_contents(static::_getExchangePath()));

        // Transform the $currency variable into an instance of
        // currency object from the ol' JSON file

        if (!isset($exchange->currencies->$currency)) {
            throw new AbacusException("Currency not found");
        }

        // Get the DateTime for when the exchange was updated last
        $this->updated_at = new \DateTime($exchange->updated->date);

        return $exchange->currencies->$currency;
    }

    private function _mapCurrency($currency)
    {
        $this->rate = $currency->rate;
        $this->decimal_separator = '.';
        $this->thousands_separator = ',';

        $this->symbol = $currency->symbol;
        $this->name = $currency->name;
        $this->symbol_native = $currency->symbol_native;
        $this->decimal_digits = $currency->decimal_digits;
        $this->rounding = $currency->rounding;
        $this->code = $currency->code;
        $this->name_plural = $currency->name_plural;
    }

    /**
     * Update Abacus's exchange rates
     *
     * Update the contents of abacus_exchange.json by polling the
     * OpenExchangeRates API, getting the currencies in currencies.json
     * and combining the two.
     *
     * @param string|null $key API Key
     *
     * @return int|false The number of bytes written to file (or false)
     *
     */
    public static function update($key = null)
    {
        $latest = self::_fetchAPI($key);

        $currencies = self::_getCurrencies();

        foreach ($currencies as $name => &$currency) {
            if (isset($latest->rates->$name)) {
                $currency->rate = $latest->rates->$name;
            } else {
                unset($currencies->$name);
            }
        }

        return self::_setCurrencies($currencies, $latest->timestamp);
    }

    /**
     * Fetch API
     *
     * Get the exchange rates from the API. If
     * no API key is supplied, it will look for one as an environment
     * variable.
     *
     * @param $key
     *
     * @throws AbacusException
     *
     * @return OpenExchangeResponse|false
     */
    private static function _fetchAPI($key)
    {
        if (is_null($key)) {
            $key = getenv('ABACUS_OPEN_EXCHANGE_KEY');
        }

        if (!$key) {
            throw new AbacusException('Undefined API key');
        }

        $url = "http://openexchangerates.org/api/latest.json?app_id=$key";

        $curl = curl_init($url);
        curl_setopt_array($curl, array(
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER => false,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_ENCODING => "",
            CURLOPT_USERAGENT => "Abacus",
            CURLOPT_AUTOREFERER => true,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_MAXREDIRS => 10
        ));

        $result = curl_exec($curl);

        if (curl_errno($curl)) {
            return false;
        };

        // Must have a correct API key in order to function properly
        if (curl_getinfo($curl, CURLINFO_HTTP_CODE) === 401) {
            throw new AbacusException("Invalid API key");
        }

        return json_decode($result);
    }

    /**
     * Get Currencies
     *
     * Get the contents of the currencies.json file and decode it
     * into a stdClass object
     *
     * @return object|false
     */
    private static function _getCurrencies()
    {
        return json_decode(file_get_contents(__DIR__ . "/../currencies.json"));
    }

    /**
     * Set Currencies
     *
     * Write the currencies, along with their exchange rates to
     * the exchange.json file in the temp directory.
     *
     * @param \stdClass $currencies
     * @param int|null $timestamp
     *
     * @return int
     */
    private static function _setCurrencies(\stdClass $currencies, $timestamp = null)
    {
        $file = new \stdClass();

        $file->updated = (new \DateTime('UTC'))->setTimestamp($timestamp);
        $file->currencies = $currencies;

        return file_put_contents(static::_getExchangePath(), json_encode($file));
    }

    /**
     * Get Exchange Path
     *
     * Get the path of the abacus_exchange.json file which contains the polled exchange information
     *
     * @return string
     */
    private static function _getExchangePath()
    {
        return sys_get_temp_dir() . "/abacus_exchange.json";
    }
}