Créer un flux ATOM en PHP structure encodage et format des dates

atom.jpgPour coder un flux RSS ou un flux ATOM sur un site, il faut d'abord savoir comment l'organiser. Ne nous prenons pas la tête dans les normes et standards, spécifications RFC... Dotclear l'a fait. Sous forme d'un fichier de template, c'est plus facile à appréhender. Dans un premier temps j'utilise ces derniers, en ajoutant mes commentaires pour faire ce mémo. Plus loin, j'ai noté la façon de générer les dates au bon format et d'encoder le contenu des éléments du flux.

Structure

l’entête

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xml:lang="fr">

<title type="html">__LE TITRE DU FLUX ENCODÉ POUR L'XML__</title>
<subtitle type="html">__LA DESCRIPTION DU SITE ENCODÉ XML__</subtitle>
<link href="__LA PROPRE ADRESSE DE CE FLUX__" rel="self" type="application/atom+xml"/>
<link href="__L'URL DU SITE__" rel="alternate" type="text/html">   title="__LE TITRE DU SITE__"/>
<updated>__LA TOUTE DERNIERE DATE DE MODIFICATION__</updated>
<author>
<name>__L'AUTEUR__</name>
</author>
<id>L URL DU SITE</id>
<generator uri="__BAH HEU L'ADRESSE DE VOTRE SITE__">__SELFMADE__</generator>

Chaque élément du flux:

chaque élément du flux commence par <entry> et est cloturé par </entry>

<entry>
<title>LE TITRE DE LELEMENT</title>
<link href="SON URL" rel="alternate" type="text/html" title="SON TITRE" />
<id>SON URL</id>
<published>DATE DE PUBLICATION</published>
<updated>LA DERNIERE DATE DE MODIFICATION</updated>
<author><name>NOM DE L'AUTEUR</name></author>
<dc:subject>SUJET, LA CHEZ DOTCLEAR ILS METTENT LA CATEGORIE</dc:subject>
<dc:subject>SUJET IDEM, UN TAG PAR EXEMPLE</dc:subject>
<content type="html">LE CONTENU</content> 
<link rel="enclosure" href="URL DE L'EVENTUELLE PIECE JOINTE" length="SA TAILLE" type="SON TYPE" />
<wfw:comment>L'URL DES EVENTUELS COMMENTAIRES</wfw:comment>
<wfw:commentRss>LE FLUX  DES COMMENTAIRES</wfw:commentRss>
</entry>

Et on termine notre fichier par:

</feed>

Headers HTML

Le fichier Php, apache va l'envoyer avec comme mime type "text/html", il suffit de préciser dès le début du script php:

header('Content-type: application/atom+xml');

Le format des dates.

C'est le format ATOM, et je n'ai pas trouvé comment faire en sorte que MySQL le fournisse de la bonne manière directement. Pour récupérer le timestamp UNIX (format différent du timestamp MySQL), on fait la requête de cette manière:

SELECT UNIX_TIMESTAMP(createdtime) .....

createdtime est le nom de la colonne.

En en PHP pour obtenir la date au format ATOM à partir du timestamp unix, c'est simplement comme ça:

$DateAtom=date(DATE_ATOM, $VariableTimestampUnix);

L'encodage de l'HTML

Bah oui, on fournit des éléments au format html qui est un langage balisé tout comme le XML (l'ATOM est du XML),Pour qu'il n'y ait pas de confusion, il faut modifier ça.

Tout ce qui se trouve dans des balises indiquant type="html" ou type="text/html" doit etre traité préalablement. La function htmlentities de PHP doit être utilisée, avec l'option ENT_QUOTES. de cette manière:

htmlentities($string,ENT_QUOTES);

Quelques liens:

Conclusion

Si j'ai écrit ce billet c'est parce que j'ai mis un flux ATOM sur un site abandonné (edit de ce post en 2017)

Bon la structure de la base de données n'est pas un secret., voici en exemple le script PHP qui génère le flux ATOM de l'image board. Si ça peut aider de l'avoir sous les yeux.

<?php 
// by Gnieark 01/2011 http://blog-du-grouik.tinad.fr
header('Content-type: application/atom+xml'); 
echo '<?xml version="1.0" encoding="utf-8"?>'; ?>
<feed xmlns="http://www.w3.org/2005/Atom"
 xmlns:dc="http://purl.org/dc/elements/1.1/"
 xmlns:wfw="http://wellformedweb.org/CommentAPI/"
 xml:lang="fr">
<title type="html">Image Board Tinad</title>
<subtitle type="html">Board tinad est un image board, un site de partage de medias, francophone, sans inscription, anonyme</subtitle>
<link href="http://board.tinad.fr/atom.php" rel="self" type="application/atom+xml"/>
<link href="http://board.tinad.fr" rel="alternate" type="text/html" title="Image Board Tinad"/>
<?php
function username($string)
{
	if ($string == ''){
		return 'Anonyme';
	}else{
		return ($string);
	}
}
function contenu($id,$img,$comment)
{
	//je fais le choix de mettre les commentaires dans le cotenu de l'élément sur ce site
	$html="<p><a href="http://board.tinad.fr/images/".$img.""><img src="http://board.tinad.fr/images/thumb-".$img."" alt="image"/></a><br />".$comment."</p>";

	$rscomment=mysql_query("SELECT createdtime, media_filename, media_fileext,media_hasthumb,titre,commentaire,user_pseudo,id 
						FROM reponses 
						WHERE parent='".$id."' ORDER BY createdtime DESC");
	while ($rcomment=mysql_fetch_row($rscomment))
	{
		$html.="<p><b>R&eacute;ponse de ".username($rcomment[6])." ".$rcomment[0]."</b></p><p>";
		if ($rcomment[1]!= "")
		{
			$html.="<a href="http://board.tinad.fr/images/".$rcomment[1].".".$rcomment[2]."">
				<img src="http://board.tinad.fr/images/thumb-".$rcomment[1].".".$rcomment[2]."" alt="img" /></a><br />";
		}
		$html.=$rcomment[5]."</p>";
	}
	return $html;
}
include ("config.php");
if (!mysql_connect($config['mysql_host'], $config['mysql_user'], $config['mysql_password'])) {
	echo 'Impossible de se connecter à MySQL';
	exit;
}
mysql_query("USE ".$config['mysql_database']);

//Pour récupérer la date de modification la plus récente:
$rupdtedtime=mysql_query("SELECT UNIX_TIMESTAMP(updatetime) FROM posts ORDER BY updatetime LIMIT 0,1");
$rsupdtedtime=mysql_fetch_row($rupdtedtime);
$updtedtime=$rsupdtedtime[0];

//Requete pour trouver les 20 derniers éléments
$sql="SELECT 
		UNIX_TIMESTAMP(createdtime), media_filename, media_fileext, titre, commentaire, id,user_pseudo, nbe_reponses, UNIX_TIMESTAMP(updatetime)
	FROM
		posts
	ORDER BY
		createdtime DESC
		LIMIT 0,20";
$rs=mysql_query($sql);


?>
	<updated><?php echo date(DATE_ATOM, $updtedtime);?></updated>
	<author>
	      	<name>Gnieark</name>
	</author>
      <id>http://board.tinad.fr/</id>
      <generator uri="http://board.tinad.fr/">SELFMADE</generator>
<?php
while ($r=mysql_fetch_row($rs))
{
//Là commence l'affichage des elements:
?>
	<entry>
	      <title>Post <?php echo $r[5]." Par ".htmlentities(username($r[6])." - ".$r[3],ENT_QUOTES); ?></title>
      		<link href="http://board.tinad.fr/index.php?post=<?php echo $r[5]; ?>" rel="alternate" type="text/html" title="<?php echo htmlentities($r[3]." Par ".username($r[6]),ENT_QUOTES); ?>" />
		<id>http://board.tinad.fr/index.php?post=<?php echo $r[5]; ?></id>
     		<published><?php echo date(DATE_ATOM, $r[0]);?></published>
		<updated><?php echo date(DATE_ATOM, $r[8]);?></updated>
      		<author><name><?php echo username($r[6]); ?></name></author>
     		 <content type="html"><?php  echo htmlentities(contenu($r[5],$r[1].".".$r[2],$r[7]),ENT_QUOTES); ?></content>
      </entry>			 
<?php
}
?>

</feed>

Commentaires

1. Le vendredi, février 4 2011, 12:52 par Ellny

Pour la date au format Atom, il suffit d'enregistrer avec date("c") dans un champ de la BDD

2. Le vendredi, février 4 2011, 17:46 par gnieark

oui, à condition d'enregistrer la date dans un champs text ou varchar Intéressant, pour se simplifier les conversions.

La limite, c'est que tu ne pourras plus faire de ORDER BY tadate, car ton champs texte il va le classer par ordre alphabétique. mais en effet si ton appli n'en a pas besoin, faut pas se géner.

3. Le samedi, février 19 2011, 19:33 par coenonympha

Bonsoir,

merci pour ce tutorial bien sympa et très complet ! J'ai justement à cette occasion suivi ce tuto pour créer le flux RSS du site de mon école. Tout marche à peu près mais j'ai l'impression que le flux ne marche pas très bien suivant les applications. Par exemple, le marque page dynamique de Firefox ne m'affiche aucun article, pareil quand j'essaye d'importer le flux dans un groupe LinkedIn...

Pourriez-vous m'expliquer d'où cela peut venir ?

Voici l'adresse du flux : http://imac.alwaysdata.net/rss.php .

Merci par avance

4. Le samedi, février 19 2011, 22:47 par gnieark

Bonjour,

Tous les éléments de votre flux ont le même id
http://imac.alwaysdata.net/index.ph...

Vu la façon dont est fait votre site, c'est pas évident car chaque actualité n'a pas une URL propre. Peut être qu'en modifiant un peu la structure du site et en ajoutant des ancres aux titres des actualités, puis dans le flux atom fournir dans l'id l'URL avec le lien vers l'ancre genre http://imac.alwaysdata.net/index.ph...

ça passera mieux au niveau des agrégateurs

5. Le dimanche, février 20 2011, 10:49 par coenonympha

Bonjour,

merci de votre réponse rapide, j'ai donc modifié en conséquence la structure de ma page d'actualité, elle peut maintenant prendre un id en paramètre pour afficher la news correspondante. Je génère donc maintenant dans mon flux des id propres à chaque article mais mon problème reste entier : curieusement le marque page dynamique firefox ne m'affiche tjs pas d'articles :(.

6. Le dimanche, février 20 2011, 11:53 par Gnieark

De chez moi ça marche:

iwacrss.jpg

Et ça passe sans erreur au w3c validate:
http://validator.w3.org/feed/check....

Et j'ai testé sous google reader, c'est OK aussi.

Je rajouterai juste un petit détail, pour chaque entry de préciser le link alternate:

par exemple:
<link href="http://board.tinad.fr/index.php?pos..." rel="alternate" type="text/html" title="poney Par Anonyme" />

ça permet d'avor un lien vers la page originale depuis le flux

7. Le dimanche, février 20 2011, 17:39 par coenonympha

Bonsoir,

les marques pages dynamiques marchent maintenant que j'ai ajouté le lien. Par contre, l'importation de ce flux reste tjs un problème dans des applications comme facebook ou linkedin mais, je m'en passerai : ce n'est pas grave.

Merci en tout cas pour votre aide.

8. Le dimanche, février 20 2011, 22:54 par gnieark

Bonsoir, de rien au contraire, ça me fait plaisir d'avoir des retours, Ellny et toi se sont appuyés sur ce billet, c'est plutot cool.

Pour facebook, J'ai fait divers essais, à la moindre erreur html dans les contenus des billets, au moindre caractere qu'il n'aime pas, ou si certaines balises ont un style qui est indiqué dans la balise et pas dans le CSS, il rejete tout le flux.

Il y a peu de flux qui passent par facebook, et je me demande si ce n'est pas une stratégie de leur part... autant avant ils étaient dans un objectif d'attirer les gens vers facebook (donc s'ouvraient aux flux), à présent ils sont dans l'idée de les maintenir captif. donc éviter qu'ils rédigent ailleurs et inciter à la création d'applications facebook dont ils ont le contrôle.

Une solution alternative pour publier automatiquement sur facebook, c'est d'utiliser ping fm (il a des défauts) ou de coller le lien à la main à chaque nouvel article.

Page top