Получение курсов валют

Получение курсов валют — задача, которая встречается очень часто. Курсы могут понадобиться чтобы просто показать их на веб-странице. Другой не менее распространённый случай — необходимость пересчитать цену в разных валютах. Какой бы ни была цель, важно чтобы курсы были получены из надёжного источника. Не менее важна их актуальность.

Самый надёжный и актуальный источник курсов валют — сайт Центробанка РФ. Через него можно получить доступ к XML-документу с курсами. Для этого нужно обратиться по URL ‘http://www.cbr.ru/scripts/XML_daily.asp’. В документе будет курсы валют на текущую дату. Также можно добавить к адресу параметр date_req, который позволяет явно указать дату, на которую нужны курсы. Тогда URL будет выглядеть следующим образом: ‘http://www.cbr.ru/scripts/XML_daily.asp?date_req=d.m.Y’. d.m.Y — это формат даты: день.месяц.год.
Кстати, PHP прекрасно понимает такие форматы дат.
XML-документ с курсами валют имеет следующую структуру: внутри корневого элемента находятся тэги <Valute>. Внутри каждого такого тэга находятся тэги, содержащие информацию о конкретной валюте. Для примера приведу скриншот XML-документа:

Рисунок 1

Тэгов внутри каждого элемента <Valute> довольно много, но чаще всего нужны 2 — <CharCode> и <Value>. С первым всё понятно, а второй — это курс валюты к рублю.
Таким образом, чтобы получить нужную нам информацию, надо сначала получить из PHP доступ к корневому XML-элементу, а потом перебрать все вложенные в него элементы <Valute>. Для каждого из них нужно просмотреть содержимое тех тэгов, которые описывают нужные нам свойства валюты.
Получить доступ ко вложенным XML-элементам из PHP совсем не сложно. Элементы можно получить по названию тэга с помощью функции getElementsByTagName(). Она возвращает коллекцию подходящих элементов. Для элементов <Valute> надо просто перебрать эту коллекцию в цикле, как уже говорилось выше. Когда мы ищем элементы, хранящие свойтва наподобие <CharCode>, внутри элемента <Valute>, функция getElementsByTagName() вернёт коллекцию из одного элемента. К нему можно обратиться следущим образом: item(0). Текст внутри тегов XML-элемента хранит свойство объекта nodeValue.
Теперь рассмотрим код примера на PHP:

<html>
<?php
//Xml-документ с курсами валют к рублю с сайта ЦБ РФ
$xml = new DOMDocument();
$url = 'http://www.cbr.ru/scripts/XML_daily.asp?date_req=' . date('d.m.Y');

//Пробуем скопировать xml-документ с курсами валют с сайта ЦБ РФ на наш сервер
$local_exchange_rates = "exchange_rates.xml";
@copy($url, $local_exchange_rates);

//Поддерживаемые валюты и их курс к рублю
$supported_currencies = ['USD' => NULL, 'EUR' => NULL];

//Загружаем xml-документ с курсами
$is_loaded = false;
if (@$xml->load($url))
  $is_loaded = true;
elseif (@$xml->load($local_exchange_rates))
  $is_loaded = true;

//Обновляем курсы поддерживаемых валют
if ($is_loaded)
{
  $root = $xml->documentElement;
  $currencies = $root->getElementsByTagName('Valute');
  foreach ($currencies as $currency)
  {
    $char_code = $currency->getElementsByTagName('CharCode')->item(0)->nodeValue;
    if (array_key_exists($char_code, $supported_currencies))
    {
      $value = $currency->getElementsByTagName('Value')->item(0)->nodeValue;
      $supported_currencies[$char_code] = floatval(str_replace(',', '.', $value));
    }
  }
  foreach ($supported_currencies as $key => $value)
  {
    echo $key.' '.$value.'<br>';
  }
}
else
  echo "Не удалось загрузить курсы валют";
?>
</html>

Стоит обратить внимание на 2 вещи:

  • Мы выводим курсы только для тех валют, которые отметили как поддерживаемые. Для этого мы объявили массив $supported_currencies и поместили туда буквенные коды тех валют, которые хотим отслеживать.
  • При каждом обращении к сайту ЦБ РФ мы сохраняем копию XML-документа локально. Если при следующем обращении к сайту ЦБ РФ он будет недоступен, то мы возьмём данные из локальной копии.
    Рисунок 2

    Эта система очень проста, но далеко не идеальна. Если предыдущее обращение было давно, то пользователь получит неактуальные курсы валют. Более сложный, но и более надёжный способ — вынести код создания локальной копии в отдельную программу и запускать эту программу как задание с определённой периодичностью. Но в этом посте мы не будем рассматривать как это сделать.