Zjišťování aktuální měnových kurzů v PHP

Internet nám mimo jiné umožňuje efektivně oslovovat zákazníky z celého světa. Představme si jednu hypotetickou situaci. Máte webový obchod, ve kterém prodáváte nějaké unikátní výrobky (třeba speciální sádrové trpaslíky), které jinde sehnat nejdou. Američan si chce nějakého speciálního trpaslíka zakoupit, narazí na vaši stránku, ale místo cen v nějaké jemu známé měně (USD) vidí jen čísla doplněná zkratkou CZK nebo Kč. Jak zajistit, aby si američan mohl zobrazit cenu v amerických dolarech, a ta byla pořád aktuální?

V tomto článku si ukážeme, jak přepočítávat ceny zboží podle aktuálního kurzu. Základem je najít vhodný zdroj dat. K tomuto účelu nám vhodně poslouží stránky České národní banky, na které najdeme odkaz na textový soubor, ve kterém jsou uloženy aktuální kurzy:

http://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.txt

Adresu souboru s kurzy si uložíme do konstanty SOUBOR_KURZY. Tuto konstantu je potřeba definovat před spuštěním následujících funkcí, jinak ukázka nebude fungovat.

Define("SOUBOR_KURZY", "http://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.txt");

Tento soubor má velmi jednoduchý formát – na jednom řádku jsou informace o jedné měně, a to v pořadí země, název měny, množství měny, třípísmenný kód měny a kurz. Na prvním řádku je ještě informace o dni, pro který jsou kurzy platné. Pro zjištění kurzu můžeme použít následující funkci:

  function zjistiKurz($mena) {
    // vstupnim parametrem je tripismenny kod meny, jejiz kurz chceme zjistit
    $kurzy = file(SOUBOR_KURZY);
    foreach ($kurzy as $v) {
      $h = explode("|", $v);
      if ((count($h) >= 5) && ($h[3] == $mena)) {
        return $h[2]." ".$h[3]." = ".$h[4]." CZK";
      }
    }
  }

Chceme-li například zjistit, jaký je kurz pro Euro, zavoláme echo zjistiKurz("EUR");. Výsledkem je 1 EUR = 28,145 CZK.

Chceme-li převést konkrétní částku v cizí měně na české koruny, použijeme následující funkci:

  function prevedNaKoruny($mena, $mnozstvi = 1, $long = false) {
    // vstupnimi parametry jsou tripismenny kod meny a mnozstvi (vychozi hodnota je 1 jednotka)
    // a take prepinac, ktery urci, jaky chceme vystup - zda-li jen cislo ($long == false)
    // nebo "dlouhy zapis"
    $kurzy = file(SOUBOR_KURZY);
    foreach ($kurzy as $v) {
      $h = explode("|", $v);
      if (isset($h[4])) $h[4] = str_replace(",", ".", $h[4]); // prevod desetinne carky na tecku
      if ((count($h) >= 5) && ($h[3] == $mena)) {
        if ($long) return "$mnozstvi ".$h[3]." = ".($mnozstvi * $h[4] / (float)$h[2])." CZK";
        else return ($mnozstvi * $h[4] / (float)$h[2]);
      }
    }
    return -1; // zadana mena nebyla nalezena
  }

Zavoláme přepočet 10 EUR na české koruny – echo prevedNaKoruny("EUR", 10, true); a dostaneme výsledek 10 EUR = 281.45 CZK. Chtěli-li bychom výsledek vhodný například pro zobrazení v e-shopu, zavoláme funkci následovně – echo prevedNaKoruny("EUR", 10); a dostaneme pouze číselnou hodnotu 281.45.

Poslední možností je převedení finanční částky z českých korun do cizí měny.

  function prevedNaCizi($mena, $pocetKorun = 1, $long = false) {
    // vstupnimi parametry jsou tripismenny kod meny a mnozstvi korun (vychozi hodnota je 1 Kc)
    // a take prepinac, ktery urci, jaky chceme vystup - zda-li jen cislo ($long == false)
    // nebo "dlouhy zapis"
    $kurzy = file(SOUBOR_KURZY);
    foreach ($kurzy as $v) {
      $h = explode("|", $v);
      if (isset($h[4])) $h[4] = str_replace(",", ".", $h[4]); // prevod desetinne carky na tecku
      if ((count($h) >= 5) && ($h[3] == $mena)) {
        if ($long) return $pocetKorun." CZK = ".($pocetKorun / (float)$h[4] * $h[2])." $mena";
        else return ($pocetKorun / (float)$h[4] * $h[2]);
      }
    }
    return -1 // zadana mena nebyla nalezena
  }

Výše uvedená funkce nám třeba převede 100 Kč na Eura – echo prevedNaCizi("EUR", 100, true);. Výsledkem je 100 CZK = 3.55 EUR. Chtěli-li bychom, aby výstup z funkce byl použitelný například v internetové prodejně, zavoláme funkci následovně – echo prevedNaCizi("EUR", 100); – výsledkem bude jen číselná hodnota, tedy 3.55

U navštěvovanějších internetových obchodů by mohlo opakované načítání jednak zbytečně zatěžovat server ČNB, druhak způsobovat zbytečné prodlevy při zobrazování webových stránek. Proto je nanejvýš vhodné soubor s kurzy stahovat pouze čas od času – například jednou za hodinu pomocí úlohy v cronu a ve skriptech načítat lokální kopii tohoto souboru.

Kurzy exotičtějších měn

Skript je taktéž možno doplnit o možnost přepočtu kurzů exotičtějších měn – k tomu nám poslouží následující soubor:

http://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_ostatnich_men/kurzy.txt

Formát je stejný jako u výše uvedeného souboru, liší se jen četnost aktualizací – soubor s exotickými měnami je vydáván jednou měsíčně (vždy na konci měsíce), zatímco ostatní měny jsou aktualizovány každý pracovní den.

Tanto příspěvek byl zařazen do kategorie PHP a označen štítkem , . Do oblíbených si můžete uložit trvalý odkaz.

28 komentářů k Zjišťování aktuální měnových kurzů v PHP

  1. lukas napsal(a):

    chtel bych upozornit ze strankach CNB pravdepodobne doslo k zmene url .
    Funkcni odkaz je (7.11.2008) http://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.txt

  2. admin napsal(a):

    Díky za upozornění, odkazy v článku jsem aktualizoval.

  3. Michal napsal(a):

    Díky moc za návod, možná by stálo za to (pro nás, neználky) do toho zapracovat i nějaký kontrolní mechanizmus, zda url existuje 🙂

  4. cestmir napsal(a):

    Dobrý den.Je to super, jen mám problém.Nedokážu přijít na to, proč při volání fce echo „prevedNaKoruny(„EUR“, 10, true);“ se mi vždy zobrazí celá, zaokrouhlená částka, což vede k velkým nepřesnostem při převodu.Ideální by byla dvě desetiná místa.Co s tím?

  5. admin napsal(a):

    cestmir: kód pro funkci prevedNaKoruny() byl špatný, protože nefungoval převod desetinné čárky na tečku – teď už je to opraveno a vrací ho hodnoty na dvě desetinná místa

  6. Tom napsal(a):

    Diky za script, bylo by mozne doplnit jak do skriptu doplnit aktualni datum zobrazovane na kurz. listku cnb?

  7. Duk napsal(a):

    Perfektní skript.

  8. Jura napsal(a):

    To prave vsude hledal :o)
    Bylo by mozne zjistit ten script complekt?

  9. admin napsal(a):

    Jura: prosím ještě jednou, česky a srozumitelně

  10. grim napsal(a):

    díky mockrát za ušetření hodiny práce 😉

  11. Lukáš napsal(a):

    Mám problém, na mém localním serveru to funguje bez problému, ale jak to nahodím na webový server (webhosting onebit, verze php 5.2.9) tak to nefunguje. Když jsem se snažil zjistit kde se to zarazí, tak hned na začátku při načítání toho textového souboru do proměnné. Proměnná je totiž prázdná. Prosím o pomoc.

  12. admin napsal(a):

    @Lukáš: tipuji zakázané allow_url_fopen, bez konkrétní chybové hlášky se lepší odpovědi nedočkáš

  13. Lukáš napsal(a):

    Ano je to na serveru zakázané.

  14. admin napsal(a):

    Lukáš: v tom případě je potřeba povolit, případně to zkusit obejít pomocí curl

  15. Milan napsal(a):

    doporučuji k práci s měnou a přepočtem využít http://addons.nette.org/cs/cnb pracuje i bez nette frameworku

  16. Frekvence stahování kurzového lístku ČNB napsal(a):

    POZOR: netřeba stahovat kurzový lístek ani s frekvencí jedenkrát za hodinu. Stačí pouze jednou denně, lístek vychází vždy jednou v každém pracovním dni vždy mezi 14:15 až 14:30.

  17. Miki napsal(a):

    Tak jsem se s tím dost natrápil. jak píše někdo nahoře CHYBA je v „,“ místo „.“

    function zjistiKurz() {
    // vstupnim parametrem je tripismenny kod meny, jejiz kurz chceme zjistit
    // $kurzy = file(‚kurz.txt‘);
    $kurzy = file(SOUBOR_KURZY);
    foreach ($kurzy as $v) {
    $h = explode(„|“, $v);
    if ((count($h) >= 5) && ($h[3] == ‚EUR‘)) {
    return str_replace(„,“,“.“,$h[4]);
    }
    }
    }

  18. Obejít to přes curl napsal(a):

    Ahoj, díky za super článek. Můžete tu někdo napsat jak to obejít přes CURL?
    Zkoušel jsem to takto:
    function prevedNaCizi($mena, $pocetKorun = 1, $long = false) {

    $ch = curl_init(SOUBOR_KURZY);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $kurzy = curl_exec ($ch);
    curl_close ($ch);

    #$kurzy = file(SOUBOR_KURZY);
    foreach ($kurzy as $v) {
    $h = explode(„|“, $v);
    if (isset($h[4])) $h[4] = str_replace(„,“, „.“, $h[4]); // prevod desetinne carky na tecku
    if ((count($h) >= 5) && ($h[3] == $mena)) {
    if($long) return $pocetKorun.“ CZK = „.($pocetKorun / (float)$h[4] * $h[2]).“ $mena“;
    //else return round(($pocetKorun / (float)$h[4] * $h[2]), 2);
    else
    $euro = ($h[4]*1.03);
    return round(($pocetKorun / (float)$euro * $h[2]), 2);
    }
    }
    return -1; // zadana mena nebyla nalezena
    }

    ale hlásí mi to chybu „Invalid argument supplied for foreach() in….“

    Díky

  19. Martin napsal(a):

    Můžete tu někdo napsat jak to obejít přes CURL?
    Díky

  20. Jiriczek napsal(a):

    Protoze curl_exec nevraci pole.

  21. smety napsal(a):

    Díky za skript! 🙂

  22. szs napsal(a):

    Díky za návod, ale hledám, zda neexistuje možnost zjistit kurz ke konkrétnímu datu. Nevíte někdo? Díky!

  23. szs napsal(a):

    Aha, tak už jsem to našel, stačí to dopsat jako parametr k URL, např. http://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.txt?date=02.01.2014

  24. Alex napsal(a):

    Ahoj, i já přidávám své poděkování za skvělý návod. Využil jsem tento kód v jednom pluginu pro WordPress. Plugin mi zobrazuje produkty a při tom ukazuje přepočítané ceny. Bohužel při zobrazení např 12 produktů se stránka načítá strašně dlouho,což se dá předpokládat. Jsou dvě řešení, buď se nastaví např 5 produktů na stránku, což svým způsobem řeší problém, nebo bych byl rád kdyby jste mi poradili, jak vyřeším načtení kurzovního lístku jen jednou za den. Děkuji

  25. Ondřej napsal(a):

    Alex

    file_get_contents
    fwrite

  26. Roman napsal(a):

    Nevím, jestli to mám správně, ale aby se mi script spojil pouze jednou denně, stahuju si data z čnb na server přesně v 15:00, jelikož (jak tady psal „Frekvence stahování kurzového lístku ČNB“) se kurzovní lístek aktualizuje mezi 14:15 – 14:30). Mám tedy podmínku if (date(‚m.d.Y H:i:s‘) === date(‚m.d.Y 15:00:00′))
    {
    unlink($_SERVER[“DOCUMENT_ROOT] .’/cesta_k_souboru/soubor.txt‘);
    file_put_contents($_SEVER[‚DOCUMENT_ROOT‘] .’/cesta_k_souboru/soubor.txt‘, fopen(cnb_listek_url – constanta, viz výše), ‚r‘));
    }
    za podmínkou script pokračuje a čte data z mého serveru.

    Pokud má někdo lepší řešení, napiště…:)

  27. Roman napsal(a):

    Spojení probíhá pouze pokud se aktualizuje stránka – je tedy potřeba na serveru nastavit cron.:)

  28. Jurix napsal(a):

    A co když potřebuji konvertovat např. EUR na USD?
    Existuje elegantnější varianta, než volat prevedNaKoruny(‚EUR‘) a pak prevedNaCizi(‚USD‘)?

Napsat komentář

Vaše emailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *