PHP pro pokročilé – ICONV a RSS čtečka

29. června 2004

Pomocí knihovny ICONV můžeme v PHP velmi snadno převádět dokument v určité znakové sadě na sadu jinou bez toho, abychom si sami psali převáděcí funkce. Různé znakové sady používají například RSS kanály a tak i RSS čtečka musí umět převést jejich znakovou sadu na znakovou sadu pro společný výstup. Jak tohoto docílit, včetně funkční RSS čtečky, si ukážeme v tomto článku.

Knihovna ICONV

Nejdůležitější funkce knihovny je iconv(), kterou si blíže popíšeme. Funkce iconv() má tři parametry. Prvním je původní kódování dokumentu, druhý parametr udává výstupní znakovou sadu a obsahem třetího parametru je text, který se má převést.

Funkce iconv() má ale několik nedostatků. Ten hlavní se týká znakové sady, která začíná řetězcem WIN. Místo něj musíte použít řetězec CP (například místo WIN-1250 použijete CP1250). Druhý problém se vyskytl, pokud funkce hlásí chybu illegal input sequence. V tomto případě ještě za znakovou sadu uveďte řetězec //TRANSLIT (například CP1250//TRANSLIT). Tento řetězec můžete ale použít, i když se chyba nevyskytne (což je výhoda – prostě ho napíšete všude). Někdy je také dobré použít ISOXXXX-X místo ISO-XXXX-X. Verze knihoven se liší a tak musíte trochu experimentovat s tím, kde a jaký řetězec použít.

Příklad převodu dokumentu ze znakové sady WIN-1250 do UTF-8 bude tedy vypadat následovně:

$retezec = „ščřžýáíé“;
$retezec = iconv(„CP1250“, „UTF-8“, $retezec);

RSS čtečka

RSS kanály patří dnes ke zcela běžné výbavě internetových medií. Umožňují jednoduše zobrazit přehled aktuálních článků z časopisů, například na vašich stránkách. K tomu ale potřebujeme takzvanou RSS čtečku. Nejprve si ukážeme příklad RSS kanálu:

<?xml version=“1.0″ encoding=“iso-8859-2″?>
<rss version=“0.91″>
 <channel>
  <title>Můj obchod.CZ</title>
  <link>http://mujobchod.cz</link>
  <description>Internetový obchod</description>
  <language>cs</language>
  <webmaster>webmaster@mujobchod.cz</webmaster>
  <image>
   <title>Logo</title>
   <url>http://mujobchod.cz/logo.png</url>
   <link>http://mujobchod.cz </link>
   <width>60</width>
   <height>60</height>
   <description>Internetový obchod</description>
  </image>
  <item>
   <title>Výrobek 1</title>
   <link>http://mujobchod.cz/prvni.php</link>
   <description>Výrobek 1 je moderním produktem ve své kategorii</description>
  </item>
  <item>
   <title>Výrobek 2</title>
   <link>http://mujobchod.cz/druhy.php </link>
   <description>Výrobek 2 plně předčí výrobek 1 v mnoha věcech</description>
  </item>
 </channel>
</rss>

V ukázce můžete pěkně vidět strukturu RSS zdroje. Každý RSS soubor musí obsahovat kódování (<?xml version="1.0" encoding="utf-8"?>), verzi RSS (<rss version="0.91">) a jeden nebo více samotných kanálů (<channel>). Obsahem kanálu je titulek (<title>), popis (<description>), odkaz na web (<link>) a jazyk (<language>), nepovinně může obsahovat navíc logo (<image>), copyright (<copyright>), kontakt na webmastera (<webmaster>) a podobně. Důležité jsou ale jednotlivé položky kanálů (<item>), které musí obsahovat titulek položky (<title>), odkaz na samotný článek (<link>) a nepovinně popis (<description>), který většinou obsahuje stručnou ukázku článku. (Podrobnější popis jednotlivých elementů RSS kanálů můžete najít v článku RSS? RSS! od Jiřího Bureše.)

RSS zdroj je XML dokument, který se dá zpracovat XML parserem. Ne na všech stanicích ale parser je k dispozici a také práce s ním je složitější. Naše čtečka RSS dokument doslova rozpitvá slovo po slovu a vrátí objekt, který obsahuje jednotlivé položky kanálů a logo zdroje. Přitom využije toho, že některé řetězce se v RSS mohou vyskytnout jen jednou (například <rss version="0.91">), můžeme tak zjišťovat informace o RSS kanálu.

Základem naší čtečky je funkce ContentElement(). Ta vrací text mezi počátečním a koncovým tagem. Tři parametry určují celý řetězec (element), počáteční tag a koncový tag. V následujícím příkladu vypíše funkce řetězec „Nadpis“:

$retezec=“<h1>Nadpis</h1>“;
echo ContentElement($retezec, „<h1>“, „</h1>“);

Kód funkce ContentElement() vypadá takto:

function ContentElement($string,$starttag,$endtag) {
 //nejprve vypočteme délky tagů
 $len_starttag=strlen($starttag);
 $len_endtag=strlen($endtag);
 //pozice počátečního tagu
 $poz_starttag=strpos($string,$starttag);
 //v části za počátečním tagem najdeme koncový tag
 $poz_endtag=strpos($string,$endtag,$poz_starttag+$len_starttag);
 //vnitřek obou tagů, zjišťujeme pozici prvního znaku po počátečním tagu a délku vnitřku tagů
 $obsah=substr($string,$poz_starttag+$len_starttag,$poz_endtag-$poz_starttag-$len_endtag+1);
 return $obsah;
}

Naši čtečku budou tvořit tři třídy:

  • clsRSS – celý RSS zdroj
  • clsImage – logo kanálu (podtřída třídy clsRSS)
  • clsItem – jednotlivá položka (podtřída třídy clsRSS)

Nejprve si ukážeme kód posledních dvou tříd. Třída clsImage popisuje logo RSS kanálu:

//třída popisující obrázek (logo) RSS kanálu
class clsImage {
 var $imagestring; //obsah elementu obrázku
 //konstruktor, parametr je vnitřek elementu <image> vytáhnutý z RSS souboru
 function clsImage($str) {
  $this->$imagestring=$str;
  //titulek obrázku (max 100 zn.)
  $this->Title=ContentElement($this->$imagestring,“<title>“,“</title>“);
  //URL obrázku (max 500 zn.)
  $this->Link=ContentElement($this->$imagestring,“<link>“,“</link>“);
  //popis obrázku
  $this->Description=ContentElement($this->$imagestring,“<description>“,“</description>“);
  //šířka obrázku (1-144, 88 defaultně)
  $this->Width=ContentElement($this->$imagestring,“<width>“,“</width>“);
  //výška obrázku (1-400, 31 defaultně)
  $this->Height=ContentElement($this->$imagestring,“<height>“,“</height>“);
 }
}

Třída clsItem popisuje položku RSS kanálu:

//třída popisující jednotlivé položky RSS kanálu
class clsItem {
 var $itemstring;
 //konstruktor, parametr je vnitřek elementu <item> vytáhnutý z RSS souboru
 function clsItem($str) {
  $this->$itemstring=$str;
  //titulek položky (max 100 zn.)
  $this->Title=ContentElement($this->$itemstring,“<title>“,“</title>“);
  //link položky (max 500 zn.)
  $this->Link=ContentElement($this->$itemstring,“<link>“,“</link>“);
  //ukázka položky (max 500 zn.)
  $this->Description=ContentElement($this->$itemstring,“<description>“,“</description>“);
 }
}

Konstruktorům obou tříd se zadají obsahy elementů <image> a <item>, které jsou získány funkcí ContentElement(). Vzhledem k tomu, že RSS obsahuje více položek <item>, jednotlivé objekty clsItem budou tvořit pole.

Celý objekt RSS je přístupný pomocí třídy clsRSS. Jejímu konstruktoru se zadá adresa RSS zdroje a nepovinně výstupní kódování (jestliže se nezadá, kódování se nezmění). Třída kontroluje i existenci zdroje pomocí následující konstrukce:

 if (!@fclose(@fopen(„$cesta“, „r“))) {
  die („RSS kanál „.$cesta.“ nelze otevřít.“);
 }

Soubor se otevře a zavře. Pokud vše proběhlo v pořádku, soubor existuje, jinak neexistuje. Funkci file_exists() nelze v tomto případě použít, protože neumí přistupovat ke vzdáleným souborům.

Kód třídy clsRSS vypadá takto:

class clsRSS {
 var $rsssoubor;
 //do konstruktoru se nastaví cesta k RSS souboru; vytvoříme proměnnou, jejímž obsahem je obsah RSS kanálu
 function clsRSS($cesta,$vystupnikodovani=““) {
  //kontrola existence souboru. Nelze použít file_exists() – neumí přistupovat ke vzdáleným souborům
  if (!@fclose(@fopen(„$cesta“, „r“))) {
   die („RSS kanál „.$cesta.“ nelze otevřít.“);
  }
  $this->$rsssoubor=implode(„“,@file($cesta));
  //verze XML a kódování
  $xmlcontent=ContentElement($this->$rsssoubor,“xml“,“>“);
  $this->XMLver=ContentElement($xmlcontent,“version=\““,“\““);
  $this->Encoding=ContentElement($xmlcontent,“encoding=\““,“\““);
  //verze RSS
  $rsscontent=ContentElement($this->$rsssoubor,“<rss“,“>“);
  $this->RSSver=ContentElement($rsscontent,“version=\““,“\““);
  //převod do zadaného kodování
  if ($vystupnikodovani!=““) {
   $r=iconv($this->Encoding.“//TRANSLIT“, $vystupnikodovani.“//TRANSLIT“, $this->$rsssoubor);
   //když se převedlo, změníme, jinak původní kódování ponecháme
   if ($r) {
    $this->$rsssoubor=$r;
   }
  }
  //titulek RSS kanálu
  $this->Title=ContentElement($this->$rsssoubor,“<title>“,“</title>“);
  //cesta ke zdroji RSS kanálu
  $this->Link=ContentElement($this->$rsssoubor,“<link>“,“</link>“);
  //popis RSS kanálu
  $this->Description=ContentElement($this->$rsssoubor,“<description>“,“</description>“);
  //jazyk RSS kanálu
  $this->Language=ContentElement($this->$rsssoubor,“<language>“,“</language>“);
  //copyrigh RSS kanálu
  $this->Copyright=ContentElement($this->$rsssoubor,“<copyright>“,“</copyright>“);
  //den RSS kanálu
  $this->Day=ContentElement($this->$rsssoubor,“<day>“,“</day>“);
  //hodina RSS kanálu
  $this->Hour=ContentElement($this->$rsssoubor,“<hour>“,“</hour>“);
  //datum poslední modifikace RSS kanálu
  $this->LastBuiltDate=ContentElement($this->$rsssoubor,“<lastbuiltdate>“,“</lastbuiltdate>“);
  //email daného webu
  $this->ManagingEditor=ContentElement($this->$rsssoubor,“<managingeditor>“,“</managingeditor>“);
  //objekt logo webu
  $str=ContentElement($this->$rsssoubor,“<image>“,“</image>“);
  $this->Image=new clsImage($str);
  //nyní zbývá vytvořit pole položek
  //to provedem tak, ža vždy odtrhneme část před <item>
  $pocetitemu=substr_count($this->$rsssoubor,“<item>“);
  $odriznuto=$this->$rsssoubor;
  for ($p=1; $p<=$pocetitemu; $p++){
   $str=ContentElement($odriznuto,“<item>“,“</item>“);
   $pole[$p] = new clsItem($str);
   $pozitemu=strpos($odriznuto,“<item>“)+6;
   $odriznuto=substr($odriznuto,$pozitemu);
  }
  $this->Item=$pole;
 }
}

Vypsání obsahu RSS zdroje pomocí naší čtečky je již velmi jednoduché:

<?
//vložíme třídy potřebné pro čtečku
include („rssreader.php“);
//adresa RSS zdroje
$zdroj=“rss.xml“;
//vytvoříme objekt čtečky
$rss = new clsRSS($zdroj,“CP1250″);
//napíšeme nadpis zdroje, link na něj a popis
$obsah.=“<h1><a href=\““. $rss->Link .“\“ title=\““. $rss->Description .“\“>“. $rss->Title .“</a></h1>\n“;
//vypíšeme jednotlivé příspěvky a vložíme je do stránky
foreach ($rss->Item as $item) {
 $obsah.=“<h2><a href=\““. $item->Link .“\“>“. $item->Title .“</a></h2>\n“;
 $obsah.=“<p>“.$item->Description.“</p>\n“;
}
?>
<html>
 <head>
  <meta http-equiv=“Content-Language“ content=“cs“ />
  <meta http-equiv=“Content-Type“ content=“text/html; charset=windows-1250″ />
 </head>
 <body>
  <?echo $obsah;?>
 </body>
</html>

Na závěr se můžete podívat na funkční RSS čtečku (čte se RSS soubor ze začátku článku), případně si stáhnout knihovnu, ve které je kód čtečky. Upozorňuji však, že pokud máte funkční XML parser, je samozřejmě výhodnější používat parser než naši čtečku, která navíc neumí například rozlišit více kanálů v jednom RSS dokumentu. I když takovou drobnost si jistě zvládnete dopsat sami.

Odkazy, zdroje

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Další článek hotelarkada.cz
Štítky: Články

Mohlo by vás také zajímat

Nejnovější

6 komentářů

  1. Nemeji

    Čvn 17, 2010 v 10:33

    Nepoužitelné – je zde hromada chyb volání fonkce ve třídě!!! např.

    $this->$rsssoubor namísto $this->rsssoubor

    a také zíškání kódování souboru:

    $this->Encoding=ContentElement($xmlcontent,“encoding=\““,“\““);

    je nedostatečně ohraničeno, lze to upravit takto:

    $this->Encoding=ContentElement($xmlcontent,“encoding=\““,“\“?\>“);

    Odpovědět
  2. Miroslav Kucera

    Čvn 17, 2010 v 11:24

    Vám přijde alespoň trochu smysluplné reagovat na šest let starý článek? ;-)

    Odpovědět
  3. Wolwino

    Zář 14, 2010 v 18:55

    Mi osobně ten komentáš užitečný byl .)

    Odpovědět
  4. Miroslav Kučera

    Zář 15, 2010 v 9:12

    Wolwino: ono to spise bylo zamysleno tak, ze ctenari se casto v diskusi vyjadruji vecem, ktere uz dnes treba nefunguji (protoze vyvoj vseho mozneho jde prudce kupredu), ale ktere byly poplatne a funkcni v dobe vydani clanku.

    Odpovědět
  5. urso

    Pro 6, 2010 v 13:36

    Po 6 letech je to již jinak. Lze velmi pohodlně použít v PHP5 simplexml:

    if ($xml = @simplexml_load_file(„http://www.efekta.cz/rss.php“)) {
    $ck = 0; // cislo komentáře
    foreach ($xml->channel->item as $item) {
    $odkaz = „zpravy_podr.php?ck=“.$ck.“&adresa=“;
    $nazev = htmlspecialchars($item->title);
    echo „“;
    echo „„.$nazev.“„;
    $ck++;
    }
    }

    Odpovědět
  6. jenna.cz

    Lis 8, 2014 v 11:51

    :-)

    Odpovědět

Napsat komentář

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