Для проектов связанных с финансами, торговлей, банками, да и в принципе любой экономической деятельностью часто бывает необходимость в получении курса валют разных стран. Причем эти данные должны быть корректными и ежедневно обновляемыми. Поэтому в сегодняшней статье я предлагаю рассмотреть скрипт парсера, который сможет получить такую информацию и вывести ее на наш сайт.

За источник мы возьмем сайт Центробанка, поскольку он всегда доступен и ежедневно обновляет данные о курсах валют. Получать данные будем в XML, а разбирать с помощью PHP расширения SimpleXML.

Сам скрипт небольшой и состоит всего из одного класса Currency:

<?php
class Currency
{

    /** @var text Источник официального курса валют */
    static public $sourceUrl = 'http://www.cbr.ru/scripts/XML_daily.asp';

    /** @var SimpleXMLElement */
    static private $xml;

    /**
     * Возвращает курс рубля к указанной валюте.
     *
     * @param text $сode Код валюты, курс которой надо вернуть.
     * @param text $default Значение при ошибке получения курса.
     * @return text
     */
    static public function rate($сode, $default = '-')
    {
        if (false === $xml = self::getXml())
            return $default;

        foreach ($xml->Valute as $valute)
            if ($valute->CharCode == strtoupper($сode))
                return $valute->Value;

        return $default;
    }

    /**
     * Получает XML источника.
     *
     * @return SimpleXMLElement|false
     */
    static private function getXml()
    {
        if (self::$xml === null)
            self::$xml = simplexml_load_string(file_get_contents(self::$sourceUrl));

        return self::$xml;
    }

}

Давайте посмотрим как работает класс и для примера выведем курс доллара и евро:

Курс ЦБ РФ на <?= date('d.m.Y') ?><br />
Доллар: <?= Currency::rate('usd') ?><br />
Евро: <?= Currency::rate('eur') ?>

Как видно на примере выше нужно указать буквенный код нужной валюты. С помощью функции strtoupper указываемый код регистронезависимый. Если такой код не найден функция вернет значение, которое указано вторым параметром.

Курс валюты на заданную дату

По умолчанию парсится курс на текущую дату. Но если мы хотим получить курс за любую другую прошедшую дату, тогда нужно немного переписать класс Currency:

<?php
class Currency
{

    /** @var text Источник официального курса валют */
    protected $sourceUrl;

    public function __construct($sourceUrl = null)
    {
        $this->sourceUrl = $sourceUrl ? $sourceUrl : 'http://www.cbr.ru/scripts/XML_daily.asp';
    }

    /**
     * Возвращает курс рубля к указанной валюте.
     *
     * @param text $сode Код валюты, курс которой надо вернуть.
     * @param text $date Дата, за которую надо вернуть курс. Формат dd.mm.yy.
     * @param text $default Значение при ошибке получения курса.
     * @return text
     */
    public function rate($сode, $date = null, $default = '-')
    {
        $url = $this->sourceUrl . ($date ? "?date_req=$date" : '');
        if (false === $content = file_get_contents($url))
            return $default;

        /** @var $xml SimpleXMLElement */
        if (false === $xml = simplexml_load_string($content))
            return $default;

        foreach ($xml->Valute as $valute)
            if ($valute->CharCode == strtoupper($сode))
                return $valute->Value;

        return $default;
    }

}

И пример получения курса доллара за сегодняшнюю дату и за пять дней назад:

<?php
$today = date('d.m.Y'); // сегодняшняя дата
$fiveDaysAgo = date('d.m.Y', time() - $dayInSeconds = 86400 * 5); // пять дней назад
?>
Доллар на <?= $today ?>: <?= (new Currency)->rate('usd') ?><br />
Доллар на <?= $fiveDaysAgo ?>: <?= (new Currency)->rate('usd', $fiveDaysAgo) ?>

Здесь может возникнуть вопрос: "Почему класс Currency теперь не статический?". Потому что, когда класс был статическим он получал контент единожды - при первом вызове. А все последующие вызовы получали контент уже из переменной класса self::$xml. Таким образом, получение курса за разные даты было невозможным из-за того, что класс всегда бы возвращал результат по первой запрошеной дате.

Теперь запрос на сайт источника идет при каждом вызове. Конечно несколько вызовов на одну дату, существенно снизят производительность. В этом случае можно либо вернуть переменную self::$xml с ключами по датам, либо для каждого вызова работать с отдельным экземпляром класса Currency. Приводить пример первого варианта не буду, поскольку его реализация - это изменение нескольких строчек кода статического класса. А вот второй вариант приведу ниже.

Курсы валют заданных стран

Допустим нам нужен скрипт, который бы выводил текущий курс по заданным странам. Для этого правим наш код под новые задачи:

<?php
class Currency
{

    /** @var text Источник официального курса валют */
    protected $sourceUrl;

    public function __construct($sourceUrl = null)
    {
        $this->sourceUrl = $sourceUrl ? $sourceUrl : 'http://www.cbr.ru/scripts/XML_daily.asp';
    }

    /**
     * Возвращает курс рубля к указанным валютам.
     *
     * @param text $сodes Коды валют, курс которых надо вернуть.
     * @param text $date Дата, за которую надо вернуть курс. Формат dd.mm.yy.
     * @param text $default Значение при ошибке получения курса.
     * @return array
     */
    public function rate(array $сodes, $date = null, $default = '-')
    {
        $url = $this->sourceUrl . ($date ? "?date_req=$date" : '');
        if (false === $content = file_get_contents($url))
            return $default;

        /** @var $xml SimpleXMLElement */
        if (false === $xml = simplexml_load_string($content))
            return $default;

        $results = [];
        foreach ($xml->Valute as $valute)
            if (in_array($valute->CharCode, $сodes))
                $results[] = (object) [
                    'name'    => $valute->Name,
                    'value'   => $valute->Value,
                    'nominal' => $valute->Nominal
                ];

        return $results;
    }

}

Теперь выводим список нужных нам валют:

<?php
$currency = (new Currency)->rate(['USD', 'EUR', 'CNY']);
foreach ($currency as $valute)
    echo "{$valute->value} рублей за {$valute->nominal} {$valute->name}<br />";

Вот таким простым способом мы можем получать ежедневно обновляемый и актуальный курс валют. Который можем использовать для различных расчетов, построений графиков или просто отображать в виде виджета на сайте.