Получение курсов валют — задача, которая встречается очень часто. Курсы могут понадобиться чтобы просто показать их на веб-странице. Другой не менее распространённый случай — необходимость пересчитать цену в разных валютах. Какой бы ни была цель, важно чтобы курсы были получены из надёжного источника. Не менее важна их актуальность.
Самый надёжный и актуальный источник курсов валют — сайт Центробанка РФ. Через него можно получить доступ к 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-документа:
Тэгов внутри каждого элемента <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-документа локально. Если при следующем обращении к сайту ЦБ РФ он будет недоступен, то мы возьмём данные из локальной копии.
Эта система очень проста, но далеко не идеальна. Если предыдущее обращение было давно, то пользователь получит неактуальные курсы валют. Более сложный, но и более надёжный способ — вынести код создания локальной копии в отдельную программу и запускать эту программу как задание с определённой периодичностью. Но в этом посте мы не будем рассматривать как это сделать.