Afficher un flux Rss avec l’objet Dom de php5

Afficher un flux Rss avec l’objet Dom de php5

Difficulté

php xml javascripthtmlcss

Un flux RSS ou Atom est un document de format xml qui contient des balises normalisées contenant le titre, le lien vers l’actualité et la description de l’actualité.

Ce sont les contenus de ces balises que nous allons récupérer pour les afficher, et nous allons utiliser le DOM de php5, une librairie incluse par défaut à php5
J’ai choisi d’afficher le flux de ce site avec une petite « bulle » affichant le début de la description de l’actu.

Voici le résultat (on passe sa souris sur les liens pour voir la bulle apparaître !) :

1. La classe DomDocument de php 5

Nous allons utiliser la classe DomDocument de php 5 pour charger le flux (format xml).

La librairie dom repose sur la librairie libxml. Les 2 sont disponibles depuis php5 et activées par défaut.

 $dom= new DOMDocument();
 $dom ->preserveWhiteSpace = FALSE ;
 $dom->load('http://www.unpointvirgule.fr/feed/');

On créée un objet DOMDocument, et on charge ensuite le flux passé en paramètre avec load
A noter : preserveWhiteSpace permet de ne pas afficher les espaces multiples, parfois générés par l’application qui créée le flux RSS.

Ensuite, on crée la variable pour stocker les données dans un tableau.

 $html_tab = array();

2. La stucture du flux

Sans vouloir expliciter la structure d’un document rss (qui est un document xml normalisé), voici le contenu de notre flux (volontairement simplifié) :

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>unPointVirgule</title>
	<atom:link href="http://www.unpointvirgule.fr/feed/" rel="self" type="application/rss+xml" />
	<link>http://unpointvirgule.fr</link>
	<language>fr-FR</language>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
			<title>Jquery vs mootools pour un effet « fancy » sous vos menus</title>
			<link>http://unpointvirgule.fr/2012/03/jquery-vs-mootools-pour-un-effet-fancy-sous-vos-menus/</link>
			<comments>http://unpointvirgule.fr/2012/03/jquery-vs-mootools-pour-un-effet-fancy-sous-vos-menus/#comments</comments>
			<pubDate>Thu, 01 Mar 2012 19:38:57 +0000</pubDate>
			<dc:creator>p'tite virgule</dc:creator>
					<category><![CDATA[JQuery, mootols, etc...]]></category>

			<guid isPermaLink="false">http://unpointvirgule.fr/?p=59</guid>
			<description><![CDATA[Avec le développement des bibliothèques de script pour Javascript, il devient difficile de choisir l’utilisation de l’une ou l’autre. Aujourd’hui, je me suis penchée sur Mootools, que vous pouvez télécharger ici. J’ai appris à coder directement à l’aide des fonctions … <a href="http://unpointvirgule.fr/2012/03/jquery-vs-mootools-pour-un-effet-fancy-sous-vos-menus/" class="excerpt-more-link">Lire la suite... <span class="meta-nav">→</span></a>]]></description>
			<wfw:commentRss>http://unpointvirgule.fr/2012/03/jquery-vs-mootools-pour-un-effet-fancy-sous-vos-menus/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		</item>
		<item>
			<title>De l’utilité sémantique des tableaux html…</title>
			<link>http://unpointvirgule.fr/2012/01/de-lutilite-des-tableaux-valides/</link>
			<comments>http://unpointvirgule.fr/2012/01/de-lutilite-des-tableaux-valides/#comments</comments>
			<pubDate>Sun, 15 Jan 2012 15:16:14 +0000</pubDate>
			<dc:creator>p'tite virgule</dc:creator>
					<category><![CDATA[Webdesign]]></category>
			<category><![CDATA[Xhtml html5...]]></category>

			<guid isPermaLink="false">http://unpointvirgule.fr/?p=8</guid>
			<description><![CDATA[« Les tableaux doivent être bannis ! » On nous le dit depuis le développement du web dit « sémantique » : les tableaux doivent être bannis dans la construction des sites…. Alors on n’en parle plus… Et on ne fait plus de tableaux. … <a href="http://unpointvirgule.fr/2012/01/de-lutilite-des-tableaux-valides/" class="excerpt-more-link">Lire la suite... <span class="meta-nav">→</span></a>]]></description>
			<wfw:commentRss>http://unpointvirgule.fr/2012/01/de-lutilite-des-tableaux-valides/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

Les flux Rss contiennent au mieux ces éléments, mais dans tous les cas au minima un title et un link.
On traitera également les flux Atom (concurrent de rss), dont la structure diffère un peu :

<?xml version="1.0" encoding="UTF-8"?><feed
  xmlns="http://www.w3.org/2005/Atom"
  xmlns:thr="http://purl.org/syndication/thread/1.0"
  xml:lang="fr-FR"
  xml:base="http://unpointvirgule.fr/wp-atom.php"
   >
	<title type="text">unPointVirgule</title>
	<updated>2012-05-25T08:54:39Z</updated>

	<link rel="alternate" type="text/html" href="http://unpointvirgule.fr" />
	<id>http://unpointvirgule.fr/feed/atom/</id>
	<link rel="self" type="application/atom+xml" href="http://unpointvirgule.fr/feed/atom/" />

	<generator uri="http://wordpress.org/" version="3.5.1">WordPress</generator>
		<entry>
			<author>
				<name>p'tite virgule</name>
						</author>
			<title type="html"><![CDATA[Jquery vs mootools pour un effet « fancy » sous vos menus]]></title>
			<link rel="alternate" type="text/html" href="http://unpointvirgule.fr/2012/03/jquery-vs-mootools-pour-un-effet-fancy-sous-vos-menus/" />
			<id>http://unpointvirgule.fr/?p=59</id>
			<updated>2013-05-22T08:54:32Z</updated>
			<published>2012-03-01T19:38:57Z</published>
			<category scheme="http://unpointvirgule.fr" term="JQuery, mootols, etc..." />		<summary type="html"><![CDATA[Avec le développement des bibliothèques de script pour Javascript, il devient difficile de choisir l’utilisation de l’une ou l’autre. Aujourd’hui, je me suis penchée sur Mootools, que vous pouvez télécharger ici. J’ai appris à coder directement à l’aide des fonctions … <a href="http://unpointvirgule.fr/2012/03/jquery-vs-mootools-pour-un-effet-fancy-sous-vos-menus/" class="excerpt-more-link">Lire la suite... <span class="meta-nav">→</span></a>]]></summary>
			<link rel="replies" type="text/html" href="http://unpointvirgule.fr/2012/03/jquery-vs-mootools-pour-un-effet-fancy-sous-vos-menus/#comments" thr:count="0"/>
			<link rel="replies" type="application/atom+xml" href="http://unpointvirgule.fr/2012/03/jquery-vs-mootools-pour-un-effet-fancy-sous-vos-menus/feed/atom/" thr:count="0"/>
			<thr:total>0</thr:total>
		</entry>
		<entry>
			<author>
				<name>p'tite virgule</name>
						</author>
			<title type="html"><![CDATA[De l’utilité sémantique des tableaux html…]]></title>
			<link rel="alternate" type="text/html" href="http://unpointvirgule.fr/2012/01/de-lutilite-des-tableaux-valides/" />
			<id>http://unpointvirgule.fr/?p=8</id>
			<updated>2013-05-25T07:37:35Z</updated>
			<published>2012-01-15T15:16:14Z</published>
			<category scheme="http://unpointvirgule.fr" term="Webdesign" /><category scheme="http://unpointvirgule.fr" term="Xhtml html5..." />		<summary type="html"><![CDATA[« Les tableaux doivent être bannis ! » On nous le dit depuis le développement du web dit « sémantique » : les tableaux doivent être bannis dans la construction des sites…. Alors on n’en parle plus… Et on ne fait plus de tableaux. … <a href="http://unpointvirgule.fr/2012/01/de-lutilite-des-tableaux-valides/" class="excerpt-more-link">Lire la suite... <span class="meta-nav">→</span></a>]]></summary>
			<link rel="replies" type="text/html" href="http://unpointvirgule.fr/2012/01/de-lutilite-des-tableaux-valides/#comments" thr:count="0"/>
			<link rel="replies" type="application/atom+xml" href="http://unpointvirgule.fr/2012/01/de-lutilite-des-tableaux-valides/feed/atom/" thr:count="0"/>
			<thr:total>0</thr:total>
		</entry>
</feed>

On souhaite accéder aux éléments enfants de item pour le Rss et aux éléments enfants de entry pour l’Atom.

3.La navigation dans le flux

Le Dom de php5 utilise principalement 4 classes pour naviguer dans l’arbre DOM :
- DomNode : L’objet noeud,
- DomDocument – Un document xml (hérité de DomNode)
- DomElement – Un élément du noeud
- DomNodeList – Une liste de noeuds DomNodes

//tous les éléments item pour un rss
if($dom->getElementsByTagName('item')->length>0)
$liste = $dom->getElementsByTagName('item');
//tous les elements entry pour un Atom
else if($dom->getElementsByTagName('entry')->length>0)
$liste = $dom->getElementsByTagName('entry');

La méthode getElementsByTagName() permet d’accéder directement à tous les noeuds dont la balise est item – ou entry pour un flux ATOM -.

Notre liste de noeuds est donc maintenant ciblée et accessible par la variable $item.
On peut donc effectuer l’itération pour stocker les valeurs dans notre tableau.
On décide de n’afficher que les 5 actualités, les plus récentes, donc les 5 premières de la liste.

for($i=0;$i<5;$i++){

  //l'enfant de l'élément item n°$i
  foreach($liste->item($i)->childNodes as $child){
	$html_tab[$i]['id']=$i;
	switch ($child->tagName){
		//recup de la date de pub
		case "pubDate": //RSS
			 $laDate=$child->nodeValue;
			 $affich_date = date('d-m-Y', strtotime($laDate));
			  $html_tab[$i]['date']=$affich_date;
		break;
		case "published": //Atom
			 $laDate=$child->nodeValue;
			 $time = new DateTime($laDate);
			 $affich_date = $time->format('d-m-Y');
			 $html_tab[$i]['date']=$affich_date;
		break;

		//recup de la description de l'actu
		case "description":
		case "summary":
			 $description_tot=$child->nodeValue;
			 $description_tot = strip_tags($description_tot);
			 $description_tot =  htmlspecialchars($description_tot);
			 $description_tot = addslashes($description_tot);
			 $description = $description_tot;
			 $description = substr($description_tot,0,200);
			 $der_espace = strrpos($description, " ");
			 $description = substr($description,0,$der_espace);
			 $description=str_replace(CHR(10),"",$description);  //pour effacer les retour chariot du fichier rss
			 $description=str_replace(CHR(39),'&#39;',$description);//pour convertir les simples quotes
			 $description=str_replace(CHR(34),'&#34;',$description);//pour convertir les doubles quotes
			 if (strlen($description) < strlen($description_tot))
			 $description .= '...';
			 $html_tab[$i]['description']=$description;
		break;
		//recup du lien pour aller voir le détail de l'actu sur le site source du rss
		case "link":
			 $lien=$child->nodeValue;
			 $html_tab[$i]['lien']=$lien;
		break;
		//recup du titre de l'actu
		case "title":
			 $titre= ($child->nodeValue);
			 $html_tab[$i]['titre']=$titre;
		break;
   }
  }
}

Pour chaque noeud enfant (childNode) de item ou entry, on regarde la valeur de son tagname c’est à dire le nom de la balise, et on récupère son contenu textuel que l’on stocke dans le tableau php $html_tab

On garde donc les balises/noeuds suivants:
- title, link, description, pubDate pour RSS
- title, link, summary, published pour ATOM

A noter : quelque soit l’encodage du flux xml, l’objet DomDocument le traite en utf8 si le jeu de caractère n’est pas spécifié dans le constructeur. Autrement dit, il n’est pas nécessaire de se soucier du jeu de caractère du flux, sauf si on souhaite le traiter ensuite en ISO-8859-15.

4. Le stockage des éléments dans une variable php

Pour chaque actualité, on affichera la date, le lien avec le titre de l’actualité, et on appelle une fonction javascript au survol (hover) du lien pour afficher la bulle contenant la description.

Il va donc falloir passer comme argument à cette fonction le contenu de cette description. Il doit donc être exempt de balises html, de retour chariot et de quotes.

Ces traitements devraient également être faits pour les autres données, ne serait-ce que pour éviter les injections xss, je ne l’ai pas fait ci dans un souci de lisibilité du code…

De plus, on n’affichera que les 200 premiers caractères dans la bulle, et ce, sans couper un mot au milieu…

Voici donc le détail des traitements effectués sur cette chaine de caractères:
- On enlève les balises html avec strip_tag,
- On convertit les caractères spéciaux avec htmlspecialchars,
- On protège des quotes et doubles quotes avec addslashes
- Mais comme cela ne suffit pas (la chaîne de caractères passées à javascript ne doit contenir aucun caractère qui gêne), nous allons convertir les simples et doubles quotes avec leur symbole numérique respectif : &#39; et &#34;
Javascript n’y verra que du feu et le navigateur nous affichera les bons caractères.
- Il faut également supprimer les retours chariots du flux qui provoqueraient un retour à la ligne dans l’appel de la fonction. Il s’agit du caractère ASCII 10.
- On ne garde que les 200 premiers caractères en coupant la chaine avec substr,
- On cherche également le dernier espace de la chaine de caractère avec strrpos pour éviter de couper un mot au milieu,
- Et on ajoute « … » si la description n’est pas entière.

5. L’affichage en html/css

On va ensuite afficher les données du flux dans la page html – encodée bien sur en utf-8 ! ;)

<ul id="rss">
<?php
foreach($html_tab as $item){
	 //ecriture du code html avec l'appel à la fonction js popup pour la bulle
?>	 

		<li>
			<span class="date">
				<?php echo $item['date']; ?>
			</span>
			<a id="id<?php echo $item['id']; ?>" class="bulle" href="<?php echo $item['lien']; ?>" target="_blank" onmouseover="popup('id<?php echo $item['id']; ?>','<?php echo $item['description']; ?>','pop')" onmouseout="cachePop('id<?php echo $item['id']; ?>')">
				<?php echo $item['titre']; ?>
			</a>
		</li>

<?php					
}
?>
</ul>

La liste des actualités est affichée avec une liste html, mise en forme par les css suivant :

<style type="text/css">
<!--
#rss{
	border:1px solid #000;
	background-color:#c1b3f5;
	padding:10px;
	width:400px;
	line-height:15px;
	border-radius: 5px 5px 5px 5px;
	box-shadow: 0 0 10px rgba(0, 0, 0, 0.4) inset;
}
#rss li{
	clear:both;
	padding:0;
	margin-bottom:5px;
	list-style-type:none;
}
.date{
	font-size:0.8em;
	font-style:italic;
	color:#616161;
	padding:0 10px 5px 0;
	float:left;
}

/*bulle et popup*/
.bulle{
	position:relative;
	font-family : Georgia, Cambria, serif;
	font-size:14px;
	color:#000;
	text-decoration:none;
}
a.bulle:hover {
	background: none; /* correction d'un bug IE */
	z-index: 999;/*pour que la bulle s'affiche au dessus des autres */
	color:#ad231f;
}
.pop {
	font-family : Georgia, Cambria, serif;
	font-size:12px;
	background-color:#545454;
	padding:10px;
	position:absolute;
	width:250px;
	border:1px solid #000;
	border-radius: 5px 5px 5px 5px;
	box-shadow: 0 0 10px rgba(0, 0, 0, 0.4) inset;
	color:#FFFFFF;
	text-decoration:none;
	text-align:justify;
	width:350;
	top:30px;
	left:10px;
}
-->
</style>

La classe pop est positionnée en absolute, et sur le hover du lien, on ajoute un z-index pour être sûr que la bulle s’affiche au dessus des autres éléments.
J’ajoute quelques effets de bords arrondis et d’ombrage interne avec les propriétés des CSS3.

6. L’affichage de la bulle par le DOM de javascript

Et bien sur, l’affichage de la fameuse bulle pop par le dom de javascript :

<script type="text/javascript">

// <![CDATA[
//fct pour le onmouseover: apparition de la bulle
function popup(noeud,mes,classe){
		//nouveau noeud
		newElement=document.createElement("span");
		//crea du noeud texte avec un innerhtml pour permettre l'interpetation des balises html
		newElement.innerHTML=mes;
		//attachement du nouveau noeud au noeud parent
		document.getElementById(noeud).appendChild(newElement);
		//la classe
		newElement.className=classe;
}

//fct pour le onmouseout: suppresion de la bulle en effaçant le noeud correspondant
function cachePop(noeud){
	leNoeud=document.getElementById(noeud);
	leNoeud.removeChild(leNoeud.childNodes[1]);
}
    // ]]>
</script>

On crée un élément span d’une classe passée en argument dans lequel on greffe le texte de la description de l’actualité.
Ce qui permet d’affecter différentes classes à la bulle par exemple.
Ce span doit disparaître lorsqu’on quitte le lien, donc au mouseout, ce que fait la fonction cache pop.

7. L’objet DomXpath

Je ne peux pas terminer cet article sans vous parler de la classe DomXpath, qui va permettre la recherche directe d’un élément spécifié, d’une façon beaucoup plus fine. On l’utilisera dans des fichiers xml plus complexes.

Dans ce cas, on passe l’objet Dom au constructeur de la classe DomXpath. Pour notre exemple, nous aurions fait ainsi :

$xpath=new DomXpath($dom);
$rootNamespace = $dom->lookupNamespaceUri($dom->namespaceURI);
if(isset($rootNamespace)) {
	$xpath->registerNamespace('x', $rootNamespace);
	$query = "//x:item[position()>0][position()<=6]";
 }else{
	 $query = "//item[position()>0][position()<=6]";
 }
foreach($xpath->query($query) as $liste){
...
}

Nous devons ici déclarer l’espace de nom s’il existe.
La requête se fait ici en Xpath, un langage de requête universel basé sur la structure spécifique du xml et plus généralement de tout document balisé.
Nous pouvons cibler directement les 5 premières actualités par [position()>0][position()<=6] : du premier au sixième élément de la liste.
Je vous renvoie à la documentation de php sur le DomXpath pour plus d’explications.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>