Creer un tchat (chat) en AJAX php

bulle_tchat-406.jpgUn tchat, mélangeant les techniques suivantes: javascript, AJAX, PHP, mysql et JSON, en moins de 150 lignes. Il y a quelques mois Hempstar proposait sur ce blog sa version du chat. Comme j'ai énormément progressé dans ces langages de programmation, je me demandais si je saurai le faire, en quelques heures, en bouffant moins de ressources réseau.

Réponse: Oui, sans trop lutter; Le voici à l'aide d'une iframe:

Mises à jour fev 2012:

  • smileys (désactivables). Ceux que j'ai mis dans le package proviennent de phpBB3 http://www.phpbb.com/ en GNU GPL licence V2 (enfin je crois).
  • Les caracteres + et & n'étaient pas pris en charge. c'est résolu.
  • la barre de défilement ne descend automatiquement que s'il y a des nouveaux messages.
  • la touche e,ntrée envoie le message, pas un retour à la ligne.

Pour l'installer:

Creez la table tchat dans une base de données:

CREATE TABLE `tchat` (
`time` TIMESTAMP NOT NULL ,
`pseudo` TEXT NOT NULL ,
`message` TEXT NOT NULL ,
PRIMARY KEY ( `time` , `pseudo` ( 5 ) )
) ENGINE = MYISAM
  • Télécharger l'archive là: tchat.tar.gz
  • Décompressez la dans un dossier sur votre site
  • Editez le fichier index.php pour renseigner les paramètres de la base de données

C'est pret!

<iframe src="/tchat/index.php" width="100%" height="300px;" border="1"></iframe>

Quelques explications sur le code:

La base de données

Basique:

CREATE TABLE `tchat` (
`time` TIMESTAMP NOT NULL ,
`pseudo` TEXT NOT NULL ,
`message` TEXT NOT NULL ,
PRIMARY KEY ( `time` , `pseudo` ( 5 ) )
) ENGINE = MYISAM

le code PHP

Deux fonctions: l'enregistrement d'un nouveau message et l'envoi des informations sur les nouveaux messages dans un tableau au format JSON. Lors du refresh on n'envoie que les messages dont la date est supérieure à celle passée en variable POST $lasttime. L'enregistrement se contente d'une requête et ne retourne rien.

<?php
  //**********Options à parametrer**************
  $nombreDeMessagesGardes=50;
  $enableSmileys=true;
  $smileysPath="smilies/";
  $mysqlparam=array(
      'username'	 => '',
      'password'	=> '',
      'host'	=> 'localhost',
      'database'	=> ''
    );
  //********** Fin des options ************

    //connexion mysql
    if (!mysql_connect($mysqlparam['host'], $mysqlparam['username'], $mysqlparam['password'])) {
	erreur('Impossible de se connecter à MySQL');
	die;
    }
    mysql_query("USE ".$mysqlparam['database']);
  //Nettoyer les variables en entrée
  foreach($_POST as $keyname =>$value){
    $$keyname=mysql_real_escape_string(htmlentities(utf8_decode($value)));
  }
  if (!isset($act)){
    $act="";
  }
  switch($act)
  {
      case "refresh":
	if(!isset($lasttime)){
	  die;
	}

	$rs=mysql_query("SELECT time,pseudo,message FROM tchat WHERE time > '".$lasttime."' ORDER BY time DESC LIMIT 0".$nombreDeMessagesGardes);
        //au cas où qu'il ny ait rien dans la requete
        $messages=array();
	while($r=mysql_fetch_row($rs)){
	  $messages[]=array($r[0],$r[1],$r[2]);
	}
	$messages=array_reverse($messages);
	echo(json_encode($messages));
	die;
      break;
      case "add":
	if((!isset($pseudo)) OR ($pseudo =="") OR (!isset($message)) OR ($message=="")){
	  echo "variable vide";
	  die; //il y a un probleme, on kill le script
	}
	if($enableSmileys AND file_exists($smileysPath."smileys.php")){
	  //les smilays
	  include $smileysPath."smileys.php";
	  foreach($phpbb_smilies as $smile){
	    $message=str_replace($smile['code'],'<img src="'.$smileysPath.$smile['smiley_url'].'" alt="'.$smile['smiley_url'].'"/>',$message);
	  }
	}
	mysql_query("INSERT INTO tchat (time,pseudo,message) VALUES (NOW(),'".$pseudo."','".$message."')");
	die;
      break;

      case "":
	break;
      default:
	//aucun des cas prévu, on stoppe le script.
	die;
	break;
  }
?>

Le code javascript

Trois function, la functrion ajax utilisée par les deux autres pour faire des appels au serveur, la function pour ajouter un message et la function pour raffraichir le tchat. Cette dernière est bouclée : t=setTimeout("refreshchat(lasttime)",3000); permet de la rappeler 3 secondes plus tard (délai qui me semble un bon compromis entre la charge réseau et la fluidité).

L'array contenant les derniers messages du tchat est récupéré de cette manière:

eval ("messages = " + xhr.responseText);

Et pour les ajouter à la fin, J'utilise le DOM avec la function appendChild:

var pmessage=document.createElement("p");
	  pmessage.innerHTML='<em class="date">'+ messages[l][0] + '</em><em class="pseudo">' + messages[l][1] + ':</em>' + messages[l][2];
	  document.getElementById("tchat").appendChild(pmessage);
function onKeyEnter(key)
  {      
    if (key == 13) {
      sendmessage(document.getElementById('pseudo').value,document.getElementById('message').value);
      return true;
    }
    return false;
  }
  function Ajx() 
  {
    var request = false;
        try {request = new ActiveXObject('Msxml2.XMLHTTP');}
        catch (err2) {try {request = new ActiveXObject('Microsoft.XMLHTTP');}
          	catch (err3) {try {request = new XMLHttpRequest();}
			catch (err1) {request = false;}
           	}
        }
    return request;
  }
  function refreshchat(time)
  {
    var xhr = Ajx(); 
    xhr.onreadystatechange  = function(){if(xhr.readyState  == 4){ 
      if(xhr.status  == 200) {
	var messages= new Array();
	eval ("messages = " + xhr.responseText);
	var yaDesNouveauxMessages=false;
	for (var l in messages){
	  yaDesNouveauxMessages=true;
	  var pmessage=document.createElement("p");
	  pmessage.innerHTML='<em class="date">'+ messages[l][0] + '</em><em class="pseudo">' + messages[l][1] + ':</em>' + messages[l][2];
	  document.getElementById("tchat").appendChild(pmessage);
	  lasttime=messages[l][0];
	}
	if (yaDesNouveauxMessages){
	  //on redescend la scrollbar
	  document.getElementById("tchat").scrollTop=document.getElementById("tchat").scrollHeight;
	}
	t=setTimeout("refreshchat(lasttime)",3000);
      }else{
	alert("Error code " + xhr.status);
      }
    }};
    xhr.open("POST", "index.php",  true);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xhr.send('act=refresh&lasttime=' + time);
  }
  function sendmessage(pseudo,message)
  {
    pseudo = pseudo.replace(/&/g,"%26");
    message= message.replace(/&/g,"%26");
    pseudo = pseudo.replace(/\+/g,"%2B");
    message= message.replace(/\+/g,"%2B");
    if ((message=='') || (pseudo=='')){
      alert('Veuillez mettre un pseudo et un message');
    }
    var xhr = Ajx(); 
    xhr.onreadystatechange  = function(){if(xhr.readyState  == 4){ 
      if(xhr.status  == 200) {
	document.getElementById("message").value="";
      }else{
	alert("Error code " + xhr.status);
      }
    }};
    xhr.open("POST", "index.php",  true);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xhr.send('act=add&pseudo=' + pseudo +  '&message=' + message );
  }

Le Body HTML:

<body>
<div id="page">
<div id="tchat">
  <script type="text/javascript">
	<!--
		lasttime='0';
		refreshchat(lasttime);
	//-->
  </script>
</div>
<div id="reponse">
  <fieldset><legend>Envoyer un message</legend>
    <label for="pseudo">Pseudonyme:</label><input type="text" id="pseudo"/>
    <label for="message">Message</label><textarea onkeypress="onKeyEnter(event.keyCode);" id="message"></textarea>
    <input type="button" value="Envoyer" onClick="sendmessage(document.getElementById('pseudo').value,document.getElementById('message').value);"/>
  </fieldset>
</div>
</div>
</body>

C'est super sommaire comme tchat.

Il vous faudra probablement coder certains certains compléments, comme l'authentification des utilisateurs, faire des salons différents etc... L'objectif ici était juste de montrer que c'est pas tres dur, pas de fournir un produit fini, mais plutot un struc simple et facilement intégrable sur un blog, site ou extranet...

Je m'envoie quelques fleurs:

Au niveau de la charge serveur, il n'y a pas de session ouverte (mémoire économisée), Le retour d'information est relativement optimisé grâce au JSON. Idéalement on pourrait mettre en cache la valeur du dernier timestamp pour n'ouvrir une connexion mysql que s'il y a des messages au timestamp supérieur à celui envoyé par la navigateur (et donc dans la majorité des cas ne même pas ouvrir de connexion mysql).

Commentaires

1. Le samedi, août 27 2011, 21:43 par complementaire sante

Plutôt dur à mettre en ligne! mais cela me tente.

2. Le dimanche, août 28 2011, 10:49 par Gnieark

Bah  heu:

* Créer la base et la table

* Créer un dossier "tchat" ou autre sur son site si on ne veut pas le mettre à la racine

* Ajouter le fichier index.php donné dans ce billet (en y modifiant les parametres de connexion à la bdd)

et voila!

3. Le dimanche, août 28 2011, 22:45 par rachat de credits

Hey plustot pas mal du credit pour ce script !!

back link deleted, mais je laisse le commentaire, il est marrant. Gnieark.

4. Le lundi, août 29 2011, 19:21 par Malagasy

Il est chouette le le commentaire! "Hey plustot pas mal du credit pour ce script !!", sur les blogs on voit toujours quelque choses de marrant!

5. Le mardi, octobre 18 2011, 07:20 par Wiloooo

il est pas gourmand en ressources ?? J'ai peur de mettre un chat avec une bdd, j'ai un site de rencontres, avec 20 utilisateurs en ligne en moyenne c'est faisable sur une dedibox ?

6. Le jeudi, octobre 20 2011, 01:43 par gnieark

Salut Wiloooo

20 utilisateurs simultanés. pas de soucis sur une dédibox pour ce script dans l'état actuel. Le débit d'une dédibox est largement suffisant et la puissance de calcul aussi. même pour les premiers prix des dédibox.

mon site qcm.tinad.fr monte jusqu'à 80 utilisateurs simultanés avec un changement de page en moyenne toutes les 10 secondes et est beaucoup moins optimisé que ce tchat (sur une dédibox qui fait beaucoup d'autres choses).

Comme je le signalais en conclusion on peut encore l'améliorer au niveau de la charge processeur -> il y a surement moyen de se passer de la requete mysql s'il n'y a pas de nouveau message en gardant le timestamp du dernier message dans un fichier texte;

Au niveau de la charge réseau-> la seule optimisation que je vois c'est de mettre le script dans une iframe comme là, mais que le script soit sur un autre FQDN; car là actuellement à chaque refresh du tchat (ttes les 3 secondes) l'utilisateur renvoit en entetes de la requete le cookie placé par le blog du grouik, c'est inutile au chat (cookie placé pour moi sur l'interface admin ou si vous avez laissé un commentaire)

Les améliorations que je serai tenté de conseiller pour un coté ergonomie: c'est la transformation des liens en balises <a> et les smilays. ça rajoutera des lignes de code à l'enregistrement d'un message; mais ce serait négligeable au niveau de la charge (réseau, calculs serveur et mémoire), la majorité des ressources étant utilisées pour la lecture du tchat.

7. Le dimanche, janvier 15 2012, 11:43 par Banana

Salut,

Il y'a un petit problème c'est que l'on peut pas mettre de +. C'est un vrai problème...

A++

8. Le dimanche, février 5 2012, 18:05 par gnieark

Banana> c'est corrigé!

9. Le jeudi, février 16 2012, 12:51 par Dim

Bonjour,
C'est possible que vous faites ce tchat avec PDO ? :)
Merci d'avance.

10. Le jeudi, février 16 2012, 18:07 par gnieark

Bonjour Dim,
Non, il n'y a que deux requetes sql dans ce code, ça n'a pas d’intérêt de passer par de la programmation objet (PDO c'est de l'objet) pour ça. ce serait comme sortir un bazooka pour éliminer une mouche.

11. Le jeudi, mai 3 2012, 15:43 par Tchat

tous ces codes sont très bizarre pour moi aussi je préfère engager un développeur pour cela

12. Le jeudi, mai 31 2012, 14:28 par nathou

bonjour, je n'arrive pas a installer la table je recois ce message d'erreur:

#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1.CREATE TABLE `tchat` (2.`time` TIMESTAMP NOT NULL ,3.`pseudo` TEXT NOT NULL ,4' at line 1

pouvez vous m'aider? merci

13. Le jeudi, mai 31 2012, 15:04 par gnieark

n'embarque pas les numéros des lignes de code lors du copier collé ;)

14. Le jeudi, mai 31 2012, 15:04 par gnieark

n'embarque pas les numéros des lignes de code lors du copier collé ;)

15. Le lundi, juin 18 2012, 14:21 par PetitGeek

Salut !
J'ai un petit problème; comment faire que la base de donner (ou le texte qui s'affiche :? ) s'actualise AUTOMATIQUEMENT et SANS RECHARGER TOUTE LA PAGE !, sans qu'on doivent recharger toute la page manuellement ? Car j'ai chercher mais j'ai rien trouver dans le script. Oui, je débute...

Ce serais sympa de me répondre, merci d'avance...

16. Le mercredi, juin 20 2012, 17:58 par gnieark

PetitGeek, c'est ce qu'il fait le tchat là, il ne recharge pas toute la page.

il fait une requête AJAX régulièrement pour demander au serveur s'il y a de nouveaux messages, et il les rajoute sur la page sans la raffraichir.

17. Le mercredi, juin 20 2012, 18:29 par gnieark

PS, PetitGeek, si ta question portait sur les techniques utilisées,

C'est dur de répondre sans connaitre ta maitrise de PHP HTML javascript.

Pour faire une requette asynchrone (AJAX) un tutoriel par là http://www.siteduzero.com/tutoriel-...

Quand à l'ajout de données à l'interieur de la page, j'ai utilisé la fonction javascript appendChild je crois.

18. Le jeudi, juin 21 2012, 16:04 par SerialFF

Hello,

Merci a toi pour ce super tchat.

Je rajouterai un petit correctif possible.
Lorsque tu rentres un smiley sans espace ":)" et bien tu es obligé d'actualiser la page pour voir le message, c'est dommage. Mais bon on fait avec ;)

Merci a nouveau ;)

19. Le jeudi, juin 21 2012, 17:41 par SerialFF

Pour ma part la partie rafraichissement automatique ne fonctionne pas avec internet explorer ni firefox...
Pas d'erreur et j'ai fait qu'un test basic en important la base fourni.

Merci

20. Le jeudi, juin 21 2012, 18:01 par gnieark

zut, les tests que je fais depuis ff et ie fonctionnent chez moi.... pige pas.

SerialFF -> Je me permettrai de te contacter sur ton adresse e-mail en fin de semaine pour plus de renseignements, ça m'intrigue,

21. Le lundi, juin 25 2012, 01:42 par SerialFF

sa serait avec grand plaisir gnieark.
Je cherche aussi a récupérer des variables $_POST venant de input hidden mais sa fonctionne pas seulement sur la page du tchat. Cela vient-il de ajax ?
C'est ce que je crains car sur une autre page sa fonctionne et sur la page du tchat j'ai le droit à un variable non déclaré.
Comment puis-je procéder pour utiliser mes $_POST ? Dois-je obligatoirement utiliser des Sessions ?

Merci

22. Le lundi, juin 25 2012, 23:33 par gnieark

SerialFF -> repondu par e-mail pas nécessaire pour les sessions mais vu ton projet, oui tu en auras probablement besoin

23. Le mercredi, juin 27 2012, 21:41 par Castiel

Bonjour, j'ai voulu tester le script, le rafraichissement ne fonctionnement pas, es ce vraiment de l'Ajax ?

24. Le jeudi, juin 28 2012, 00:56 par Castiel

Autant pour moi, le rafraîchissement ne fonctionne pas sur mon Wamp mais fonctionne en ligne ^^

25. Le jeudi, juin 28 2012, 13:08 par gnieark

Castiel > faut regarder si ton WAMP supporte toutes les fonctions utilisées dans la partie PHP du code

26. Le lundi, janvier 14 2013, 22:20 par Xav54

Bonjour ou Bonsoir j'ai un projet de creer un tchat (assez lourd) avec plusieurs salons et moderateurs..Je voudrais sa voir comment faire pour ecrire les Codes et quels Serveurs utiliser merci d'avançe

27. Le mardi, janvier 15 2013, 11:42 par gnieark

Xav54 ->
Quels serveurs .... un mutualisé apache dans un premier temps suffira largement et ne te ruinera pas.

Quels codes.... celui présenté dans ce billet est une bonne base de départ, il te faudra faire pas mal d'ajouts pour les modérateurs et les fonctionnalités de ban avertissements, ajout de salons etc... Bref, si tu sais coder en PHP/javascript, en moins d'une semaine c'est torché

28. Le vendredi, janvier 25 2013, 23:17 par Netyoko

Bonjour !
Excuse moi, mais j'ai intégré le chat sur mon serveur, tout marche, sauf le refresh qui ne marche pas, je suis obligé de le faire manuellement, j'avoue ne pas comprendre pourquoi :o
Merci d'avance :

29. Le samedi, janvier 26 2013, 01:15 par Netyoko

Bizzarement ça marche de temps en temps :s

30. Le jeudi, mai 9 2013, 01:40 par Lilo

Hop

J arrive apres la bataille mais je confirme, le rafraichissement ne fonctionne pas. Sinon c est pas mal ce truc mais evidemment, ca manque d interet si il faut faire F5 ^^

31. Le jeudi, mai 9 2013, 01:46 par Lilo

tiens ba je viens de trouver pourquoi ^^

C est ADBLOCK qui bloque le javascript

32. Le jeudi, mai 9 2013, 01:53 par Lilo

bon non c est ca. Bizarre. c est aléatoire effectivement

33. Le jeudi, mai 9 2013, 02:11 par Lilo

Ligne 34 :
Il faut ajouter une initialisation de l'array au cas ou il serait vide :

$messages = array();

Plus de probleme de mon coté a priori

34. Le samedi, mai 18 2013, 20:26 par gnieark

Lilo > Je fais la correction que tu proposes dans le billet. Marci de ta contribution!

35. Le lundi, avril 14 2014, 18:15 par Le Yoshi

Y'a un problème avec le chargement quand on a envoyé un message :(

36. Le jeudi, mai 8 2014, 18:33 par Dori

Oui j'ai le même problème, lorsque j'envois un message il n'apparait pas, il faut recarger la page.

37. Le dimanche, mai 18 2014, 22:25 par gnieark

Dori> Non, désolé, je viens de tester à l'instant (je viens de l'installer sur un site afin de faire quelques tests), ça marche nickel. donne pus de détails sur ta config stp.

38. Le mardi, novembre 14 2017, 12:07 par Thug

Y a t-il une limite de charactere ?

Page top