Comment réaliser un lecteur de flux universel
Nous allons détailler les étapes de construction d'un lecteur
de flux reconnaissant tous les formats, en utilisant les possibilités
XML de PHP 5.
La connaissance de la structure d'un fichier RSS est indispensable pour cette
étude.
Cette page extensible est réalisée avec Ajax Extensible Page de Xul.fr et du framework Anaa.
Structure d'un fichier RSS
Tout fichier de syndication contient une liste d'éléments,
les articles, billets ou autres documents, et la description du site qui en
est la source, que l'on appelle le canal.
Pour le canal aussi bien que pour les éléments, on donnera un
titre et une description, ainsi qu'une URL.
Articles ou documents
Dans tous les formats, des données de bases sont reprises: le lien sur l'article, le titre, et un résumé.
<item>
<title>Tutoriels RSS</title>
<link>https://www.scriptol.fr/lecteur-de-flux.php</link>
<description>Tutoriels sur la construction et l'utilisation de flux RSS </description>
</item>
Le nom des balises est différent selon le format utilisé. D'autres données peuvent être fournies comme la date de parution, l'auteur, un logo, etc.
Le canal, ou site fournissant le contenu
Le flux comprend une description de la source, donc le site sur lequel sont publiés les documents. Son URL, le titre de la page d'accueil, une description du site.
<channel>
<title></title>
<link>https://www.scriptol.fr/</link>
<description></description>
<channel>
Ici aussi, le nom des balises dépend du format utilisé.
Les éléments représentant les articles sont placés après le description du canal, comme on le voit dans les différents formats ci-dessous.
Différences entre les formats
Une différence globale entre RSS 2.0 et Atom est que RSS utilise le
conteneur rss, et atom uniquement le canal. Les autres différences
concernent les noms des balises.
En ce qui concerne RSS 1.0 qui se base sur RDF, la syntaxe s'éloigne
nettement de celles des deux autres formats.
Format RSS 2.0
L'exemple est basé sur celui de la spécification du standard RSS 2.0 de Harvard.
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title>Xul News</title>
<link>https://www.scriptol.fr/</link>
<description>Réaliser un lecteur de flux.</description>
<language>fr-FR</language>
<pubDate>Tue, 10 Jun 2003 04:00:00 GMT</pubDate>
<item>
<title>Tutoriel</title>
<link>https://www.scriptol.fr/rss/</link>
<description></description>
<pubDate>Jeu, 28 Sep 2007 09:39:21 GMT</pubDate>
</item>
</channel>
</rss>
Format RSS 1.0 basé sur RDF
Le format 1.0 utilise les mêmes noms de balise que le 2.0 ce qui facilitera la construction d'un lecteur universel. Il y a cependant des différences de structures. En premier lieu, le conteneur rdf appartient à un espace de nom homonyme. La structure est définie dans la balise channel, mais les descriptifs des éléments sont ajoutés après cette balise.
L'exemple ci-dessous basé sur la spécification du standard RSS 1.0.
<?xml version="1.0"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/"
>
<channel rdf:about="http://www.xml.com/xml/news.rss">
<title>scriptol.fr</title>
<link>https://www.scriptol.fr</link>
<description> </description>
<image rdf:resource="https://www.scriptol.fr/images/logo.gif" />
<items>
<rdf:Seq>
<rdf:li resource="https://www.scriptol.fr/rss/" />
...autres articles...
</rdf:Seq>
</items>
</channel>
<image rdf:about="https://www.scriptol.fr/images/logo.gif">
<title>scriptol.fr</title>
<link>https://www.scriptol.fr</link>
<url>https://www.scriptol.fr/universal/images/logo.gif</url>
</image>
<item rdf:about="https://www.scriptol.fr/rss/l">
<title>RSS</title>
<link>https://www.scriptol.fr/rss/</link>
<description> </description>
</item>...autres items...
</rdf:RDF>
Même si le format est plus complexe, l'utilisation restera simple avec les fonctions XML de PHP et le DOM.
Format Atom, structure générale
Le format Atom utilise directement le canal comme conteneur racine. La balise de canal est feed et les éléments sont des entry.
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="https://www.scriptol.fr">
<title>Exemple de flux</title>
<link href="https://www.scriptol.fr/rss/"/>
<updated></updated>
<author>
<name>Denis Sureau</name>
</author>
<entry>
<title>Réaliser un lecteur de flux</title>
<link href="https://www.scriptol.fr/rss/lecteur-de-flux.php"/>
<updated></updated>
<summary>Une description.</summary>
</entry>
</feed>
On voit qu'Atom utilise des noms de balises propres tandis que les deux formats RSS partagent les mêmes, ce dont nous tirerons profit pour identifier le format d'un fichier de flux.
Utilisation de DOM avec PHP 5
Le modèle objet de documents permet d'extraire des balises dans un
document XML ou HTML. On utilisera la fonction getElementsByTagName
pour obtenir la liste des balises dont le nom est donné en paramètre.
Cette fonction retourne une liste au format DOMNodeList, qui contient
des éléments au format DOMNode.
Elle s'applique au document en entier, ou à un élément
DOMNode et permet donc d'extraire des parties du fichier, le canal
ou un élément et dans cette partie trouve une liste de balises.
Extraction du canal RSS
DOMDocument $doc = new DOMDocument("1.0");
DOMNodeList $canal = $doc->getElementsByTagName("channel");
On utilisera la paramètre "feed" pour un flux Atom. Noter que les noms de classes sont ici à titre informatif, le code PHP ne les utilise pas.
Extraction du premier élément
DOMElement $element = $canal.item(0);
On peut assigner un DOMElement à un DOMNode donc directement
à l'appel de la méthode item() qui retourne un DOMNode.
L'avantage est que DOMElement dispose d'attributs et méthodes
utilisables pour accéder au contenu de l'élément.
Extraction de tous les éléments
for($i = 0; $i < $canal->length; i++)
{
$element = $canal->item(i);
}
Utilisation des données de l'élément
Pour chaque élément, comme pour le canal, on extrait les composants avec la même méthode et avec l'attribut firstChild. Par exemple pour le titre:
$title = $element.getElementsByTagName("title"); // obtenir la liste des balises title
$title = $title->item(0); // obtenir la balise
$title = $title->firstChild->textContent; // obtenir le contenu de la balise
A défaut de méthode pour extraire un élément
seul, on utilise getElementsByTagName pour extraire une liste qui contiendra
en fait un seul élément, et avec la méthode item,
on récupère cet élément.
En XML, le contenu d'une balise est traité comme un sous-élément,
donc on utilise la propriété firstChild pour obtenir
l'élément XML du contenu, et data pour le contenu texte.
Il reste alors à appliquer ces méthodes sur le canal et sur chaque élément du flux pour en récupérer le contenu.
Pour une utilisation plus générale du lecteur de flux, la fonction implémentée retourne le contenu dans un tableau à deux dimensions. On aura ensuite le choix sur la façon de l'utiliser: l'afficher directement dans une page Web, ou effectuer un traitement sur ce tableau.
Comment identifier le format
Identifier le format est très simple si l'on sait que RSS 1.0 et 2.0
utilisent les mêmes balises, et donc que la même fonction pourrait
s'appliquer à ces deux formats. On reconnaîtra ainsi le format
Atom au conteneur feed tandis que RSS 2.0 utilise channel et
1.0 utilise rdf.
Comme les deux versions RSS utilisent la balise channel, c'est la présence
de la balise feed qui permet d'identifier ce format.
DOMDocument $doc = new DOMDocument("1.0");
DOMNodeList $canal = $doc->getElementsByTagName("feed");
$isAtom = ($canal != false);
On essaie d'extraire le canal selon la balise "feed". Si l'interpréteur trouve cette balise, la liste de type DOMNodeList contiendra donc un élément. Le drapeau isAtom est placé à true quand l'objet n'est pas vide parceque la balise est présente, sinon on traitera le flux comme étant au format RSS, sans distinction.
Lecture des données du canal
On sait donc comment extraire le canal. La même fonction peut être
utilisée avec en paramètre les chaînes "feed"
ou "channel".
On suppose que le pointeur du document est une variable globale, $doc.
function extractChannel($chan)
{
DOMNodeList $canal = $doc->getElementsByTagName($chan);
return $canal->item(0);
}
On peut alors avec la fonction suivante, appelée avec le nom de chaque balise en paramètre, lire le titre et chaque élément descriptif du canal.
function getTag($tag)
{
$content = $canal->getElementsByTagName($tag);
$content = $content->item(0);
return($content->firstChild->textContent);
}
On appelera alors successivement la fonction avec en paramètre "title", "link", "description"...
Les noms dépendront du format, ce sera "summary" ou "subtitle" pour Atom et "description" pour les autres.
Lecture des données des éléments
Le principe sera le même, mais on devra traiter en boucle une liste d'éléments tandis qu'il il n'y a qu'un seul canal.
Il faut aussi prendre en compte le fait que RSS 1.0 place les descriptions des éléments hors de la balise de canal alors qu'elles y sont contenues pour les autres formats. Les éléments sont contenus dans feed en Atom, dans channel en RSS 2.0 mais dans rdf:RDF en RSS 1.0.
La fonction extractItems extrait la liste des éléments, on lui passe en paramètre "item" pour RSS et "entry" pour Atom:
function extractItems($tag)
{
DOMNodeList $dnl = $doc->getElementsByTagName($tag);
return $dnl;
}
On utilise la liste retournée pour accéder à chaque
élément. Celui-ci est ajouté dans le tableau $a.
Example avec le format RSS.
$a = array();
$items = extractItems("item");
for($i = 0; $i < $items->length; i++)
{
array_push($a, $items->item($i));
}
On peut aussi directement créer un tableau des balises d'un élément,
title, link, description pour chaque élément
et le placer dans un tableau à deux dimensions.
Pour ce faire on utilisera une version générique de la fonction
getTag définie précédemment:
function getTag($item, $tag)
{
$content = $item->getElementsByTagName($tag);
$content = $content->item(0);
return($content->firstChild->textContent);
}
for($i = 0; $i < $items->length; i++)
{
$a = array();
$item = $items->item($i);
array_push($a, getTag($item, "title"));
... même chose pour chaque balise d'un élément ...
array_push($FeedArray, $a);
}
Nous avons ainsi placé chaque article dans un tableau à deux dimensions que l'on peut simplement afficher ou utiliser à sa guise. On placera la boucle ci-dessus dans la fonction getTags.
Les fonctions du lecteur complet
Nous avons maintenant la liste de toutes les fonctions utiles pour le lecteur universel.
ExtractChannel Extrait la balise du canal dans un objet.
ExtractItems Extrait les items du document dans un objet.
GetTag Lit le contenu d'une balise.
GetTags Place le contenu descriptif d'un élément (canal
ou article) dans un tableau.
Avec les paramètres adéquats, ces fonctions s'utilisent pour tous les formats.
Universal_Reader Englobera le processus entier pour un flux donné,
de format non spécifié.
Universal_Display Fonction customisable d'affichage du flux dans
une page HTML.
Chargement du flux
Dans le cas le plus basique, le flux est destiné à être intégré dans une page Web, soit avant le chargement, soit ultérieurement à la demande de l'utilisateur.
Quel que soit le format, et surtout pour les flux en français, il
faut prendre garde à la compatibilité du format d'encodage,
qui est le plus souvent UTF-8 pour le flux, et quelquefois ISO-8159 ou windows-1252
pour la page où s'affichera le flux.
Il est préférable de donner l'encodage UTF-8 à la page
pour éviter un mauvais affichage des caractères accentués.
L'encodage est donné par la meta de type de contenu avec une ligne au format suivant:
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
Chargement avec la page
Lorsque l'on veut afficher une page qui intègre un flux, on insère le code suivant dans le code HTML:
<?php
include("universal-reader.php");
Universal_Reader("https://www.scriptol.fr/rss.xml");
echo Universal_Display();
?>
Voir la démonstration donnée plus loin.
Chargement à la demande
Ce cas s'impose lorsque le visiteur choisit un flux dans une liste ou entre
lui-même le nom du flux.
Le chargement peut alors se faire en Ajax
pour un affichage asynchrone ou en PHP seul, en réaffichant la page.
On utilisera un formulaire avec un champ d'entrée de texte pour donner
l'URL du flux ou un simple lien (ou un choix de liens) sur lequel(s) cliquer
pour afficher un flux.
Téléchargement
Télécharger les fonctions Universal Reader dans une archive Zip.
Elle contient deux démonstrations.
Plus d'informations
- Comparer la syntaxe des formats de flux.
- Lecteur de flux RSS 2.0. Le format le plus répandu.
- Common Reader. API pour réaliser un lecteur de flux universel.