Feedarator

2010-12-26 22:46:25 Post #1 Comandeer

 
Przez święta mnie natchnęło i napisałem prostą, badziewną klasę do parsowania RSS i ATOM (pewnie do czegoś się kiedyś przyda ;D). Zapraszam do oceny - wieszajcie na mnie psy, bo pisałem to 15 minut (z czego 5 przeznaczyłem na zjedzenie kolacji).


<?php
/*
@package: VEF
@version: 0.1
@author: Comandeer
@desc: Klasa pobierająca RSS/Atom/XML (jak chcesz to możesz nawet JSON dorobić...) z podanego URI
*/
class helper_feeds_Feedarator
{
/*parse
@params: string $URI => URI feeda
@return: array $feed => zawartość feeda jako tablica
@author: Comandeer
@desc: Pobiera feeda z danego URI, obrabia go i zwraca jako tablicę
*/
public function parse($URI)
{
if(!filter_var($URI,FILTER_VALIDATE_URL)) //podany URI nie jest poprawnym linkiem
return false;
$feed=simpleXML_load_file($URI); //w końcu RSS i ATOM to poprawne dokumenty XML (w teorii...)
$type=$feed->getName(); //pobieramy nazwę roota
$method='parse'.$type;
if(method_exists($this,$method)) //jeśli parser takiego typu dokumentu istnieje (może zamiast root element brać po przestrzeni nazw? Mniejsza szansa na pomyłkę... Dupa... RSS nie ma przestrzeni nazw!)
return $this->$method($feed);
return false;
}
/*parseRSS
@params: object: SimpleXML $XML => feed RSS
@return array $feed => przeparsowana zawartość feeda
@author: Comandeer
@desc: Parsuje feeda danego jako parametr i zwraca odpowiednią tablicę
*/
private function parseRSS($XML)
{
$channel=$XML->channel;
$feed=array
(
'link'=>(string)$channel->link,
'title'=>(isset($channel->title))?(string)$channel->title:'(brak tytułu)',
'description'=>(isset($channel->description))?(string)$channel->description:'Brak opisu',
'language'=>(isset($channel->language))?(string)$channel->language:'default',
'image'=>isset($channel->image)?array
(
'title'=>(string)$channel->image->title,
'src'=>(string)$channel->image->url
):NULL
);
foreach($channel->item as $item) //jakby nie było tego tablicowego interfejsu dla obiektu SimpleXML, to sobie nie wyobrażam jego użytkowania...
$feed['items'][]=array
(
'title'=>(isset($item->title))?(string)$item->title:'(brak tytułu)',
'link'=>(string)$item->link,
'description'=>(string)$item->description,
'author'=>(isset($item->author))?(string)$item->author:'Anonim',
'pubdate'=>(isset($item->pubdate))?(string)$item->pubdate:time()
);
return $feed;
}
/*parseFeed
@params: object: SimpleXML $XML => feed Atom
@return array $feed => przeparsowana zawartość feeda
@author: Comandeer
@desc: Parsuje feeda danego jako parametr i zwraca odpowiednią tablicę
*/
private function parseFeed($XML)
{
$channel=$XML;
//roota feed może mieć trzy i trochę formatów - dla pewności sprawdzamy jeszcze przestrzeń nazw
$rattr=(array)$channel->attributes('xml');
$ns=(array)$channel->getDocNamespaces();
if(strtolower((string)$ns[''])!='http://www.w3.org/2005/atom'||strtolower((string)$ns['atom'])!='http://www.w3.org/2005/atom')
$author=(isset($channel->author))?(string)$channel->author->name:'Anonim'; //pobieramy autora feeda
foreach($channel->link as $link) //pobieramy linka do feeda
{
$attr=$link->attributes();
if(!isset($attr['rel'])||$attr['rel']=='self')
$link=(string)$attr['href'];
}
$feed=array
(
'link'=>(isset($link))?$link:NULL,
'title'=>(isset($channel->title))?(string)$channel->title:'(brak tytułu)', //nie wiem po co ten workaround, bo w ATOM title jest wymagany
'language'=>(isset($rattr['lang']))?(string)$rattr['lang']:'default', //Ha, od czego mamy atrybut xml:lang?
'description'=>(isset($channel->description))?(string)$channel->descriptionisset($channel->subtitle)?(string)$channel->subtitle:'Brak opisu') //$channel->description jest z d wyciągnięte... Subtitle szybciej będzie
);
foreach($channel->entry as $item)
{
foreach($item->link as $link) //wyciągnięcie adresu WWW nie jest zadaniem prostym... ATOM jest bardziej skomplikowany niż RSS
{
$attr=$link->attributes();
if(!isset($attr['rel'])||$attr['rel']=='alternate') //jak alternate nie prowadzi do strony WWW, to trudno... Ma prowadzić i już (przynajmniej według kursu na BrowseHappy.pl)
$link=(string)$attr['href'];
}
$feed['items'][]=array
(
'title'=>(isset($item->title))?(string)$item->title:'(brak tytułu)',
'link'=>(isset($link))?$link:'',
'description'=>(isset($item->content))?(string)$item->contentisset($item->summary)?(string)$item->summary:'Brak opisu'), //content przechowuje zawartość, summary streszczenie
'author'=>(isset($author))?(string)$author:'Anonim',
'pubdate'=>(isset($item->updated))?(string)$item->updated:time()
);
}
return $feed;
}
}

Nie bójcie się śmiesznej nazwy klasy, bo mam po prostu taką konwencję nazewnictwa (hint: własny framework z tandetnym hackiem imitującym namespace).
Przykład zastosowania?
<?php
	require_once('Feedarator.class.php');
	$f=new helper_feeds_Feedarator;
	$rss=$f->parse('http://kanaly.rss.interia.pl/fakty.xml');
	$atom=$f->parse('http://pornel.net/pornelski.atom');
	echo '<pre>RSS:';var_dump($rss);echo '</pre>';
	echo '<pre>Atom:';var_dump($atom);echo '</pre>';

Życzę miłego oceniania ;D

2010-12-27 09:16:51 Post #2 nospor

 
$feed=simpleXML_load_file($URI); //w końcu RSS i ATOM to poprawne dokumenty XML (w teorii...)

No wlasnie, w teorii.... Pamietaj by sprawdzać mimo wszystko czy sie udało pobrać czy nie. W praktyce spotkałem dużo serwisów, gdzie były to złe xml'e

"Brak opisu", "Brak tytułu"
Tu lepiej wstawiaj poprostu NULL. Nie wymuszaj jakiś tekstów.

Pozatym wydaje się że działa

2010-12-27 12:37:23 Post #3 Comandeer

 
Gdybym nie pisał tego o 23:58, pewnie bym tego ifa przy pobieraniu dorzucił ;D
Powiadasz, że NULL jest lepszy od '(brak tytułu)'? Ja jakoś wolę mieć już od razu gotowy tekst, zamiast później w czytniku bawić się ze sprawdzaniem czy tytuł jest pusty i robienia na szybko zamiennika. Zresztą - jak komuś wygodniej z NULL, to se zmieni ;D

2010-12-27 13:20:44 Post #4 nospor

 
Chodzi o to, ze ktos moze chciec miec inny tekst niz "brak tytułu". wowczas musi ifowac po Twoim "brak tytułu". A jak kiedys zmienisz klase i zamiast "brak tytulu" dasz "brak tytulku" i jak ktos zrobi upgrade klasy to bedzie mial zonk, znowy bedzie musial IFa zmieniac. NULL jest bardziej uniwersalny.

2010-12-27 21:09:07 Post #5 Comandeer

 
Nastąpił mały update
Z racji non-working BBCode, klasę można zassać stąd:
http://wklej.org/id/445665/
Przykład zastosowania:
<?php
	require_once('Feedarator.class.php');
	$f='helper_feeds_Feedarator';
	$rss=$f::parse('http://kanaly.rss.interia.pl/fakty.xml');
	$atom=$f::parse('http://pornel.net/pornelski.atom');
	echo '<pre>RSS:';var_dump($rss);echo '</pre>';
	echo '<pre>Atom:';var_dump($atom);echo '</pre>';

Co się zmieniło?
1) Klasa nie musi być dłużej instancjonowana - działa równie dobrze jako statyczna
2) Kod jest "only PHP 5.3 +" (a to za sprawą __CLASS__ - niby dupstwo, a jak wszystko zmienia ;D)
3) Dodałem tego bzdurnego if'a przy wczytywaniu feeda
4) 'Brak tytułu' i 'Anonim' zmieniłem na NULL (jakbym jakieś przeoczył, to se już end-user zmieni)
To by było tyle. Za drobną opłatą mogę pomóc w zintegrowaniu mojej klasy z istniejącymi aplikacjami webowymi

2010-12-28 08:51:23 Post #6 nospor

 
Hmmm... zaskoczyło mnie, że używasz funkcji jako statycznie, mimo iż statycznymi nie są a php nie rzuca błędem. Dlaczego nie rzuca? Zawsze do tej pory przy takich numerach mi rzucał.

2010-12-28 14:30:06 Post #7 Comandeer

 
Bodajże od 5.3.0 nie rzuca, ale nie dam se uciąć głowy.

Odpowiedz

Ostatnio na forum

  1. PHP Developer - Gdań... moze kobieta
  2. PHP Developer - Gdań... Tomek ARforce
  3. Hackathon Distribute... aleksandra_c
  4. Klasa obsługi szablo... freeboc
  5. PHP [Symfony] Develo... NewPerspective
  6. [Wrocław][PHP Develo... Software house Amsterdam Standard sp. z o.o.
  7. Senior PHP Developer... Kingit

Skrypty użytkowników

  1. Klasa obsługi szablo... Lirdoner
  2. Sekcje user76
  3. Klasa walidująca for... user76
  4. Licznik Gości online korey
  5. Form Builder Comandeer
  6. Dynamiczny licznik z... korey
  7. Captcha Comandeer