Hírfolyamok feldolgozása
Manapság sok olyan magyar weblapot látunk, ami rendelkezik hírcsatornával, ezeket viszont kevés felhasználó olvassa, használja. Pedig hasznosak a hírfolyamok, ha nem az átlag internetezőknek, akkor nekünk, fejlesztőknek, mikor más oldalakról akarunk bizonyos tartalmakat kölcsönözni.
Léteznek egymással kapcsolatban lévő intézmények (klubok, egyesületek stb.), melyek szívesen megosztanák egymással híreiket, programajánlójukat, egyebet. (Régebben divat volt az időjárás is.) Ezt legegyszerűbben úgy lehet megvalósítani, ha feldolgozzák egymás hírforrásait. Igen, de milyen a jó feldolgozó? Igényeim a következők voltak:
- A lehető legtöbb hírfolyamtípust képes legyen feldolgozni,
- mégis egységes eredményhalmazt adjon vissza, használata során lehetőleg egyáltalán ne kelljen a formátummal foglalkozni;
- szűrje a tartalmat beállítások szerint (pl. képek, egyes HTML elemek);
- ha akarom, rendezze az elemeket dátum szerint csökkenő sorrendbe;
- a dátumokat magyarul, a hét napjaival jelenítse meg;
- rövidítsen a túl hosszú bevezetőkön, a benne esetleg szereplő HTML elemek helyes lezárásával;
- lehetőleg egyszerűbb PHP függvényeket használjon, a minél jobb hordozhatóság érdekében.
Nos, olyan feldolgozót nem találtam, ami mindezt tudná, ezért fogtam a fejszét és belevágtam.
A legnagyobb feladatnak a különböző szabványok szerint készült XML dokumentumok egységes struktúrába rendezése látszott, ezért először jó sokat olvastam róluk a Wikipédián és különböző RFC-k között. Számomra úgy tűnik, hogy az RSS 2.0 a legelterjedtebb szabvány, ezért az egységesített struktúra és a változók nevei többnyire ezt követik.
class RssReader
{
var $title;
var $link;
var $description;
var $lastBuildDate;
var $pubDate;
var $ttl;
var $items;
var $errors;
private $document;
private $root;
private $itemCount;
private $tags1 = array(
'<a>', '<ol>', '<h5>',
'<b>', '<dl>', '<h6>',
'<i>', '<dt>', '<pre>',
'<u>', '<dd>', '<span>',
'<cite>', '<li>', '<th>',
'<code>', '<p>', '<td>',
'<q>', '<h1>', '<tr>',
'<strong>', '<h2>', '<tbody>',
'<div>', '<h3>', '<table>',
'<ul>', '<h4>',
);
private $tags2 = array(
'</a>', '</ol>', '</h5>',
'</b>', '</dl>', '</h6>',
'</i>', '</dt>', '</pre>',
'</u>', '</dd>', '</span>',
'</cite>', '</li>', '</th>',
'</code>', '</p>', '</td>',
'</q>', '</h1>', '</tr>',
'</strong>', '</h2>', '</tbody>',
'</div>', '</h3>', '</table>',
'</ul>', '</h4>',
);
private $days = array(
'Vasárnap',
'Hétfő',
'Kedd',
'Szerda',
'Csütörtök',
'Péntek',
'Szombat'
);
function RssReader()
{
$this->errors = array();
if (version_compare(phpversion(), '5.1.0', '>=')) {
if (!date_default_timezone_set('Europe/Budapest')) {
$this->errors[] = 'Nem sikerült az időzónát beállítani.';
}
}
if (extension_loaded('mbstring') === false) {
$this->errors[] = 'Nincs betöltve az "mbstring" modul.';
}
unset($this->document);
unset($this->root);
$this->document = new DomDocument();
$this->items = array();
}
private function resetAll()
{
$this->title = '';
$this->link = '';
$this->description = '';
$this->lastBuildDate = '';
$this->pubDate = '';
$this->ttl = 60;
unset($this->items);
$this->errors = array();
$this->itemCount = 0;
}
function getItemCount()
{
return $this->itemCount;
}
// …
}
A hírfolyam egészére vonatkozó mezők:
title
- a csatorna címe;
link
- általában a forrásweblap főoldalának a címe, ritkábban egy aloldalé vagy a hírfolyamé;
description
- a csatorna leírása, sok esetben üres;
lastBuildDate
- a hírfolyam utolsó összeállításának ideje (pl. 2011-12-15 Csütörtök, 19:20:58);
pubDate
- a hírfolyam közzétételének ideje (sok esetben csak egyik dátumot hozza a folyam, ilyenkor a két változó azonos értékű)
ttl
- a csatorna ajánlott frissítési ideje (másodpercben) – sokszor nincs (vagy rosszul van) megadva, jobb, ha mi döntjük el a feldolgozás gyakoriságát.
A hírfolyam majdani elemeinek száma a getItemCount()
függvénnyel kérdezhető le.
A konstruktor mindössze beállítja az időzónát (ha a PHP kiadás későbbi, mint 5.1.0), ellenőrzi az mbstring
kiterjesztés meglétét (az UTF-8 kódolás miatt szükséges), létrehoz egy DomDocument
objektumot, és egyes változókat alaphelyzetbe állít. Ha valami hiba történik – itt és a többi függvénynél is – arról az errors
tömbben olvashatunk (magyarul).
Ezután következhet a hírfolyam betöltése fájlból (ha a tartalmat időzítve mentjük egy könyvtárunkba), XML sztringből (ha egy változóba mentettük előzőleg) vagy közvetlenül URL alapján (ha rábízzuk az osztályra a távoli nyitást – ehhez be kell legyen kapcsolva az allow_url_fopen
):
function loadFile($filename)
{
$this->resetAll();
if (!isset($filename) or (!file_exists($filename))) {
$this->errors[] = 'Nincs megadva fájlnév, vagy a fájl nem létezik.';
return false;
}
if ($this->document->load($filename) === false) {
$this->errors[] = 'Nem sikerült a fájl betöltése, valószínűleg nem megfelelő XML dokumentum.';
return false;
}
$this->read();
return true;
}
function loadXml($xml)
{
$this->resetAll();
if ($this->document->loadXML($xml) === false) {
$this->errors[] = 'Nem megfelelő a betöltött XML sztring.';
return false;
}
$this->read();
return true;
}
function loadUrl($url)
{
$this->resetAll();
if (!allow_url_fopen) {
$this->errors[] = 'Nincs engedélyezve a távoli fájlnyitás (allow_url_fopen), így nem lehet URL-ről betölteni.';
return false;
}
$xml = file_get_contents($url);
if ($xml === false) {
$this->errors[] = 'Sikertelen a távoli fájl olvasása a "file_get_contents()" függvénnyel.';
return false;
} else {
return $this->loadXml($xml);
}
}
Mindhárom függvény false
-szal tér vissza sikertelenség esetén, ilyenkor az errors
tömböt érdemes megnézni a fent említett módon. Sikeres betöltés után meghívódik a rejtett read()
függvény, mely a gyökérelem tagName
tulajdonsága alapján eldönti, hogy a dokumentum RSS, Atom vagy RDF típusú, és meghívja az ennek megfelelő feldolgozót:
private function read()
{
$this->root = $this->document->documentElement;
if ($this->root->tagName == 'rss') {
$this->readRss();
}
if ($this->root->tagName == 'feed') {
$this->readAtom();
}
if (
(stristr($this->root->tagName, 'rdf'))
or (stristr($this->root->tagName, 'RDF'))
) {
$this->readRdf();
}
}
private function readRss()
{
$channels = $this->root->getElementsByTagName('channel');
$channel = $channels->item(0);
foreach ($channel->getElementsByTagName('title') as $s) {
if ($s->nodeValue > '') {
$this->title = htmlspecialchars($s->nodeValue);
break;
}
}
foreach ($channel->getElementsByTagName('link') as $s) {
if ($s->nodeValue > '') {
$this->link = $s->nodeValue;
break;
}
}
$this->description = '';
if (isset(
$channel
->getElementsByTagName('description')
->item(0)
->nodeValue
)) {
$this->description =
$channel
->getElementsByTagName('description')
->item(0)
->nodeValue
;
}
$time = false;
foreach ($channel->getElementsByTagName('lastBuildDate') as $s) {
if ($s->nodeValue > '') {
$time = strtotime($s->nodeValue);
break;
}
}
if ($time) {
$this->lastBuildDate =
date('Y-m-d ', $time)
. $this->days[date('w', $time)]
. date(', H:i:s', $time)
;
} else {
$this->lastBuildDate = '';
}
$time = false;
foreach ($channel->getElementsByTagName('pubDate') as $s) {
if ($s->nodeValue > '') {
$time = strtotime($s->nodeValue);
break;
}
}
if ($time) {
$this->pubDate =
date('Y-m-d ', $time)
. $this->days[date('w', $time)]
. date(', H:i:s', $time)
;
} else {
$this->pubDate = '';
}
if ($this->pubDate == '') {
$this->pubDate = $this->lastBuildDate;
}
if ($this->lastBuildDate == '') {
$this->lastBuildDate = $this->pubDate;
}
foreach ($channel->getElementsByTagName('ttl') as $s) {
if ($s->nodeValue > '') {
$this->ttl = $s->nodeValue;
break;
}
}
foreach ($channel->getElementsByTagName('item') as $s) {
$this->items[$this->itemCount]['title'] = '';
if (isset($s->getElementsByTagName('title')->item(0)->nodeValue)) {
$this->items[$this->itemCount]['title'] = htmlspecialchars(
$s
->getElementsByTagName('title')
->item(0)
->nodeValue
);
}
$this->items[$this->itemCount]['link'] = '';
if (isset($s->getElementsByTagName('link')->item(0)->nodeValue)) {
$this->items[$this->itemCount]['link'] =
$s
->getElementsByTagName('link')
->item(0)
->nodeValue
;
}
$this->items[$this->itemCount]['author'] = '';
if (isset($s->getElementsByTagName('author')->item(0)->nodeValue)) {
$this->items[$this->itemCount]['author'] =
$s
->getElementsByTagName('author')
->item(0)
->nodeValue
;
} else if (isset(
$s
->getElementsByTagName('creator')
->item(0)
->nodeValue
)) {
$this->items[$this->itemCount]['author'] =
$s
->getElementsByTagName('creator')
->item(0)
->nodeValue
;
}
$time = false;
if (isset(
$s
->getElementsByTagName('pubDate')
->item(0)
->nodeValue
)) {
$time = strtotime(
$s
->getElementsByTagName('pubDate')
->item(0)
->nodeValue
);
}
if ($time) {
$this->items[$this->itemCount]['pubDate'] =
date('Y-m-d ', $time)
. $this->days[date('w', $time)]
. date(', H:i:s', $time)
;
} else {
$this->items[$this->itemCount]['pubDate'] = '';
}
$this->items[$this->itemCount]['comments'] = '';
if (isset(
$s
->getElementsByTagName('comments')
->item(0)
->nodeValue
)) {
$this->items[$this->itemCount]['comments'] =
$s
->getElementsByTagName('comments')
->item(0)
->nodeValue
;
}
$this->items[$this->itemCount]['description'] = '';
if (isset(
$s
->getElementsByTagName('description')
->item(0)
->nodeValue
)) {
$this->items[$this->itemCount]['description'] =
$s
->getElementsByTagName('description')
->item(0)
->nodeValue
;
}
$this->itemCount++;
}
}
private function readAtom() {
$feed = $this->root;
$this->title = '';
if (isset($feed->getElementsByTagName('title')->item(0)->nodeValue)) {
$this->title = htmlspecialchars(
$feed
->getElementsByTagName('title')
->item(0)
->nodeValue
);
}
foreach ($feed->getElementsByTagName('link') as $s) {
if (
(
($s->getAttribute('rel') == 'alternate')
and ($s->getAttribute('type') == 'text/html')
)
or ($s->getAttribute('rel') == 'self')
) {
$this->link = $s->getAttribute('href');
break;
}
}
$this->description = '';
if (isset(
$feed
->getElementsByTagName('subtitle')
->item(0)
->nodeValue
)) {
$this->description = htmlspecialchars(
$feed
->getElementsByTagName('subtitle')
->item(0)
->nodeValue
);
}
$time = false;
if (isset($feed->getElementsByTagName('updated')->item(0)->nodeValue)) {
$time = strtotime(
$feed
->getElementsByTagName('updated')
->item(0)
->nodeValue
);
}
if ($time) {
$this->pubDate =
date('Y-m-d ', $time)
. $this->days[date('w', $time)]
. date(', H:i:s', $time)
;
} else {
$this->pubDate = '';
}
$this->lastBuildDate = $this->pubDate;
foreach ($feed->getElementsByTagName('entry') as $s) {
if (isset($s->getElementsByTagName('title')->item(0)->nodeValue)) {
$this->items[$this->itemCount]['title'] = htmlspecialchars(
$s
->getElementsByTagName('title')
->item(0)
->nodeValue
);
} else {
$this->items[$this->itemCount]['title'] = '';
}
foreach ($s->getElementsByTagName('link') as $ss) {
if ($ss->getAttribute('rel') == 'alternate') {
$this->items[$this->itemCount]['link'] =
$ss->getAttribute('href')
;
break;
}
}
$time = false;
if (isset(
$s
->getElementsByTagName('published')
->item(0)
->nodeValue
)) {
$time = strtotime(
$s
->getElementsByTagName('published')
->item(0)
->nodeValue
);
}
if ($time) {
$this->items[$this->itemCount]['pubDate'] =
date('Y-m-d ', $time)
. $this->days[date('w', $time)]
. date(', H:i:s', $time)
;
} else {
$this->items[$this->itemCount]['pubDate'] = '';
}
if ($this->items[$this->itemCount]['pubDate'] == '') {
$time = false;
if (isset(
$s
->getElementsByTagName('updated')
->item(0)
->nodeValue
)) {
$time = strtotime(
$s
->getElementsByTagName('updated')
->item(0)
->nodeValue
);
}
if ($time) {
$this->items[$this->itemCount]['pubDate'] =
date('Y-m-d ', $time)
. $this->days[date('w', $time)]
. date(', H:i:s', $time)
;
} else {
$this->items[$this->itemCount]['pubDate'] = '';
}
}
$this->items[$this->itemCount]['author'] = '';
if (isset(
$s
->getElementsByTagName('author')
->item(0)
->getElementsByTagName('name')
->item(0)
->nodeValue
)) {
$this->items[$this->itemCount]['author'] =
$s
->getElementsByTagName('author')
->item(0)
->getElementsByTagName('name')
->item(0)
->nodeValue
;
}
$this->items[$this->itemCount]['comments'] = '';
$this->items[$this->itemCount]['description'] = '';
if (isset(
$s
->getElementsByTagName('content')
->item(0)
->nodeValue
)) {
$this->items[$this->itemCount]['description'] =
$s
->getElementsByTagName('content')
->item(0)
->nodeValue
;
}
if (isset(
$s
->getElementsByTagName('summary')
->item(0)
->nodeValue
)) {
$this->items[$this->itemCount]['description'] .=
$s
->getElementsByTagName('summary')
->item(0)
->nodeValue
;
}
$this->itemCount++;
}
}
private function readRdf() {
$xmlns = $this->root->getAttribute('xmlns');
$rdf = $this->root->getAttribute('xmlns:rdf');
$dc = $this->root->getAttribute('xmlns:dc');
if (($xmlns == '') or ($rdf == '') or ($dc == '')) {
$this->errors[] = 'Nem szabványos RDF dokumentum.';
return false;
}
$channel = $this->root->getElementsByTagName('channel')->item(0);
$this->title = '';
if (isset(
$channel
->getElementsByTagName('title')
->item(0)
->nodeValue
)) {
$this->title = htmlspecialchars(
$channel
->getElementsByTagName('title')
->item(0)
->nodeValue
);
}
$this->link = $channel->getAttribute('rdf:about');
if (
($this->link == '')
and (isset($channel->getElementsByTagName('link')->item(0)->nodeValue))
) {
$this->link =
$channel
->getElementsByTagName('link')
->item(0)
->nodeValue
;
}
$this->description = '';
if (isset(
$channel
->getElementsByTagName('description')
->item(0)
->nodeValue)
) {
$this->description =
$channel
->getElementsByTagName('description')
->item(0)
->nodeValue
;
}
$time = false;
if (isset(
$channel
->getElementsByTagNameNS('*','date')
->item(0)
->nodeValue
)) {
$time = strtotime(
$channel
->getElementsByTagNameNS('*','date')
->item(0)
->nodeValue
);
}
if ($time) {
$this->pubDate =
date('Y-m-d ', $time)
. $this->days[date('w', $time)]
. date(', H:i:s', $time)
;
} else {
$this->pubDate = '';
}
$this->lastBuildDate = $this->pubDate;
foreach ($this->root->getElementsByTagName('item') as $s) {
$this->items[$this->itemCount]['title'] = '';
if (isset($s->getElementsByTagName('title')->item(0)->nodeValue)) {
$this->items[$this->itemCount]['title'] = htmlspecialchars(
$s
->getElementsByTagName('title')
->item(0)
->nodeValue
);
}
$this->items[$this->itemCount]['link'] = '';
if (isset($s->getElementsByTagName('link')->item(0)->nodeValue)) {
$this->items[$this->itemCount]['link'] =
$s
->getElementsByTagName('link')
->item(0)
->nodeValue
;
}
if (isset(
$s
->getElementsByTagName('creator')
->item(0)
->nodeValue
)) {
$this->items[$this->itemCount]['author'] =
$s
->getElementsByTagName('creator')
->item(0)
->nodeValue
;
} else if (isset(
$s
->getElementsByTagName('author')
->item(0)
->nodeValue
)) {
$this->items[$this->itemCount]['author'] =
$s
->getElementsByTagName('author')
->item(0)
->nodeValue
;
} else {
$this->items[$this->itemCount]['author'] = '';
}
$this->items[$this->itemCount]['comments'] = '';
$time = false;
if (isset($s->getElementsByTagName('date')->item(0)->nodeValue)) {
$time = strtotime(
$s
->getElementsByTagName('date')
->item(0)
->nodeValue
);
}
if ($time) {
$this->items[$this->itemCount]['pubDate'] =
date('Y-m-d ', $time)
. $this->days[date('w', $time)]
. date(', H:i:s', $time)
;
} else {
$this->items[$this->itemCount]['pubDate'] = '';
}
$this->items[$this->itemCount]['description'] = '';
if (isset(
$s
->getElementsByTagName('description')
->item(0)
->nodeValue
)) {
$this->items[$this->itemCount]['description'] =
$s
->getElementsByTagName('description')
->item(0)
->nodeValue
;
}
$this->itemCount++;
}
}
Ekkortól a csatorna teljes hírtartalma elérhető az items
tömb elemein keresztül:
title
- az elem címe;
link
- az elem eredetijének elérhetősége;
pubDate
- a közzététel dátuma (ez is magyarul)
author
- az elem szerzője (név, de akár e-mail is lehet)
description
- a hír szövege, ez sok esetben HTML-t is tartalmaz;
comments
- ha az elemhez tartoznak hozzászólások, akkor azok elérhetősége (a Weblabor hírfolyamába például az új fórumtémák kerülnek így).
A betöltés után, ha nem minden elemet akarunk megjeleníteni, javaslom az rsortDate()
függvény használatát, mely megfordítja az elemek (pubDate
szerinti) sorrendjét:
function rsortDate() {
for ($n = 0; $n < $this->itemCount - 1; $n++) {
for ($m = $this->itemCount - 1; $m > 0; $m--) {
if (
$this->items[$m]['pubDate']
>
$this->items[$m - 1]['pubDate']
) {
$s = $this->items[$m];
$this->items[$m] = $this->items[$m - 1];
$this->items[$m - 1] = $s;
}
}
}
}
Kitérnék itt még a a getShortText()
függvényre, mely egy elem szövegét rövidítve, szűrve adja vissza:
function getShortText($itemIndex = -1, $length = 0, $allowTags = 'all') {
$row = chr(13) . chr(10);
if (
($itemIndex < 0)
or ($itemIndex >= $this->itemCount)
or ($length < 1)
or (!isset($allowTags))
) {
$this->errors[] = 'Hibás paraméter(ek) a getShortText() függvényben.';
return false;
}
$input = $this->items[$itemIndex]['description'];
$maxlength = mb_strlen(strip_tags($input));
if ($maxlength > $length) {
$cut = $length;
$found = false;
$s2 = strip_tags($input);
$source = mb_substr($s2, $cut, 8);
while ($found === false) {
$position = mb_strpos($input, $source, $cut - 1);
if ($position === false) {
$cut += 5;
if ($cut >= $maxlength) {
$found = true;
$cut = false;
$s1 = $input;
} else {
$source = mb_substr($s2, $cut, 8);
}
} else {
$found = true;
$s1 = mb_substr($input, 0, $position);
}
}
} else {
$s1 = $input;
$cut = false;
}
if ($allowTags <> 'all') {
$s2 = strip_tags($s1, $allowTags);
} else {
$s2 = $s1;
}
$position = mb_strrpos($s2, '<');
if ($position >= mb_strlen($s2) - 8) {
$s2 = mb_substr($s2, 0, $position);
} else if ($cut) {
$position = mb_strrpos($s2, ' ');
$s2 = mb_substr($s2, 0, $position);
$position = mb_strrpos($s2, '<');
if ($position >= mb_strlen($s2) - 8) {
$s2 = mb_substr($s2, 0, $position);
}
}
if ($cut) {
$s2 .= '…';
}
for ($n = 0; $n < count($this->tags1); $n++) {
$open = mb_substr_count($s2, $this->tags1[$n], 'utf-8');
$close = mb_substr_count($s2, $this->tags2[$n], 'utf-8');
if ($open > $close) {
for ($m = 0; $m < ($open - $close); $m++) {
$s2 .= $this->tags2[$n] . $row;
}
}
}
return $s2;
}
Paraméterei:
itemIndex
- ezen sorszámú elem szövegét fogja használni;
length
- a kívánt hossz karakterben;
allowTags
- megengedett HTML elemek, ezek maradhatnak a szövegben, a többit kiveszi (ha
'all'
-t adunk meg, mindent benthagy).
A length
hosszt közelítésekkel keresi a függvény, ebbe nem számítanak bele a címkék, de a szóköz stb. igen; az esetleges entitások, speciális karakterek miatt lehet némi eltérés (kb. 5-12 karakter) a megadott és a tényleges (hasznos) hossz között. Ha a rövidítendő description
rövidebb length
-nél, akkor nem történik levágás. Egyébként a szöveg szóvégnél lesz elvágva, a függvény pótolja a szükséges záró címkéket, a szöveg végéhez pedig …-t fűz. A címkék számolásához és zárásához a $tags1
és $tags2
rejtett tömböket használja, ezeket bárki kiegészítheti szükség szerint. (Én kb. 30 db, különböző helyről származó hírforrással teszteltem, sikerrel.)
Végül – a példa kedvéért – betöltés után írjunk ki maximum öt elemet, 150 karakter maximális hosszal:
$rss->rsortDate();
if ($rss->getItemCount() > 5) {
$m = 5;
} else {
$m = $rss->getItemCount();
}
for ($n = 0; $n < $m; $n++) {
echo '<div class="feed_item">';
echo ' <h3><a href="' . $rss->items[$n]['link'] . '">' . $rss->items[$n]['title'] . '</a></h3>';
echo ' <p><strong>' . $rss->items[$n]['author'] . ' </strong> ' . $rss->items[$n]['pubDate'] . '</p>';
echo $rss->getShortText($n, 150, '<p><i><b><span><strong>');
if ($rss->items[$n]['comments'] > '') {
echo ' <p><a href="' . $rss->items[$n]['comments'] . '">Hozzászólások</a></p>');
}
echo "</div>\r\n";
}
Megfontolásra érdemes, hogy egyes webhelyek nem követik azt az ajánlást, miszerint egy hírcsatornában maximum húsz elemet jelenítsenek meg, teljes mérete pedig ne haladja meg a 300 kB-ot. Ez az osztály az egész dokumentumot egyszerre tölti be, ezért a nagyobb méretű hírfolyamok feldolgozása lassú lehet, extrém esetben memóriafoglalási hibát is előidézhet.
Fontos tudni: a hírcsatornák felhasználása nem mindig ingyenes! Minden esetben alaposan járjunk utána, hogy az adott oldal hírcsatornáját milyen feltételekkel használhatjuk fel! Ha az XML dokumentum forrásában nem találunk erre vonatkozó információt, még nem jelenti azt, hogy szabadon felhasználhatjuk. Érdeklődjünk az üzemeltetőnél.
Köszönöm Poetronak a sok hasznos információt, amely segítségével elindultam, Hidvégi Gábornak, aki a készülő osztályt ellenőrizve ellátott ötletekkel a javításhoz, valamint Joó Ádámnak, aki ezt a cikket olvashatóvá varázsolta.
A cikkben bemutatott osztály, és a hírfolyamok közzétételére szolgáló párjának forráskódja egyben letölthető. A bélyegképen a szerző fényképe látható.
■
Jobban jártál volna, ha
A cikk nagyon hasznos, én is csináltam rss olvasót, tényleg nagyon utána kell járni mindennek. Amivel még kiegészíteném, hogy nem minden feed tartja be a karakterkódolást, velem előfordult már, hogy utf-8-at írtak az XML-ben, egyébként meg teljesen más volt, szóval azt detektálni kell. A másik, ami előfordult, hogy pubDate szerint nem voltak sorbarakva a bejegyzések, szóval azt is érdemes rendezni, amikor kiolvasod őket. A feed-ek nagy átlaga azért képes használható adatot nyújtani, ezek csak extrém esetek...
Szabványok
A betöltés után, ha nem
Jah úgy néz ki, pedig emlékszem, hogy olvastam, csak már éjjel volt :-)
Ügyes. De azért a
Például ennek a cikknek a feedje ezt tartalmazza:
Hiba
Innentől kezdve ez a feed nem RSS 2.0, csak "hasonlít". Szerintem vmi miatt (talán a cikkben szereplő tag-ek?) a Drupal kutyulja össze ezt a feed-et. Mellesleg WL-ről mentett feed-del is teszteltem, tehát ez új dolog...
"A hiba az Ön készülékében
Nem tudom, hogy új-e ez a dolog, de márciusban már így volt amikor a Weblabor - Friss tartalom szkriptemmel játszottam.
Igen,
Az osztályt és a cikket január-februárban írtam, asszem két akkori WL-es feed volt a tesztben, de erről a (weblabor.hu/rss...) címről. Ha jól emlékszem.
De pl. az IE8 sem ír ki ehhez
De, akár lehetne,
Én így hirtelen IE8-al néztem meg.
Mindegy, elsősorban nem ezen (dátumon) múlik egy megjelenített tartalom olvasottsága, ha pedig mégis, akkor a tartalom előállítójának jobban érdeke a helyes feed elkészítése.
Valóban nézőpont kérdése. Én
Míg el nem felejtem, a dátum csak az egyik. A dc:creator is ugyanúgy el van rondítva a feedben, ezért dc:creator Pepita formában jeleníted meg.
A détumnál az extra html tag
Ok,
Nálam olyan 10% szokott hibás
Miért saját?
http://planetozh.com/blog/2004/11/magpierss-vs-lastrss-comparison-of-php-rss-parsers/
Miért nem éri meg sajátot írni? Mert elég sok fajta feed létezik (RSS különböző verziói, meg ott az Atom is), és elég sok dolgot le kell kezelni, ha korrekt támogatást akarsz csinálni. Még ezek a sokat próbált kódok is elhalnak néha, pl. a blogspot tud olyat, hogy nem ha nagy egy feed tartalma, akkor a végét x kB-nál simán levágja, nem törődve az XML-lel, meg azzal, hogy félbevág egy bejegyzést. Az XML parserrel még ott is tud probléma lenni, hogy vannak olyan karakter kódok, amik az XML szerint nem megengedettek, de az UTF-8 szerint igen, és simán belekerülnek egy "XML" feedbe, a parser meg dob egy hátast. Ésatöbbi. :)
Mert jónak tartom
Ez az x kB-os levágás felkeltette az érdeklődésem, lehet emiatt lesz következő verzió...
Én inkább úgy csinálnám, hogy
Igen, te
De igazad van. Nekem - egyelőre - ennyi volt a célom, ha bővebb/nagyobb/másik olvasót írok, lehet, hogy úgy csinálom. Ennél viszont szempont volt a lehető leggyorsabb lefutás (is), aminek nem kedvez a több fájlból történő betöltés. (Pl. olyan, kiegészítő jellegű felhasználáskor, mint az én oldalamon, csak nagyon kicsi erőforrást/forgalmat akarok elhasználni rá.)
Persze, más az, ha leszeded a
Igen, az már amolyan
A kód minősége?
A három féle olvasó, három hosszú függvényként tényleg nem túl elegáns, ám ennek következményeként sok apró ismétlés is van. Szinte megszámlálhatatlanul sokszor szerepel, hogy 1. változó értéke üres string, 2. létezik-e az adott node adott attribútumú eleme, 3. ha, igen, akkor legyen az az értéke. Ha semmi mást nem csinálnánk, csak ezt kiemelnénk, akkor sokkal rövidebb lenne a kód. Ahogy a dátum átalakítás is mindegyik esetben pont ugyanúgy megy.
Illetve kicsit zavar, hogy a típusok sem egészen egyértelműek, a $time először boolean, majd string. Majd utána ezt a stringet minden esetben ugyanazon a műveleten hajtjuk végre. Értem, hogy miről szól, de egy Weblabor-os mintapéldában azért mégsem szerencsés. Ahogy a $s->nodeValue > '' sem az.
Az „allow_url_fopen” az beépített konstans? Ott nem inkább ini_get('allow_url_fopen') lenne?
Engem a god object-en kívül
Minden kódnál van
- rosszabb;
- szebb / csúnyább.
Optimalizálni mindent lehet a végtelenségig, de igazad van: lehetne (kellene) szebb, rövidebb.
Köszönöm szépen a kritikát, tanultam belőle. A dátumoknál - utólag - én is gondoltam függvényre, de aztán számomra nem tűnt fontosnak. Már más a véleményem.
$time
: megengedtem magamnak a nem típusos nyelv adta előnyt.allow_url_fopen
: konstans, csakphp.ini
-ben állítható, tehát használható így.ini_get
-tel is lehetne olvasni, tetszés dolga.allow_url_fopen: konstans,
A deklarálatlan konstansok használatkor a saját nevüket kapják értékként. A helyes használat ini_get('allow_url_fopen'), de az előző mondatbeli kijelentés miatt ini_get(allow_url_fopen) is működik. Viszont allow_url_fopen nem.
Bekapcsolt allow_url_fopen estében:
Köszi, de akkor ez
if (!allow_url_fopen)
false? Mert akkor jelenlegi helyén sem működne! Esetleg különbség van kiírás és feltétel/vizsgálat között? (Nem lennék meglepve.)Nincs különbség. A feltétel
Köszönöm szépen!
Nem mentegetőzni akarok, de kikapcsolt
allow_url_fopen
esetén így ugyan nem lesz benne az$errors
tömbben a megfelelő üzenet, de lesz helyette a sikertelen távoli fájlnyitásos - és sajnos egy warningot is dobni fog.Mihelyst lesz rá időm, a zip-ben javítom. Itt a cikkben nem tudom, talán ott kéne hagyni(?).
Javítás
Rendben,
Köszönettel:
Pepita.