Cómo implementar COMET con PHP y Prototype

8
3411

Comet es una técnica de programación que permite a los servidores web enviar datos al cliente sin ninguna necesidad de que éste lo solicite. En aplicaciones AJAX ocurre lo contrario, el cliente (el navegador web) no puede ser notificado en tiempo real por el servidor si se han producido cambios en éste. Es el usuario quien deberá realizar la petición, quizás haciendo clic en un vinculo, botón ó mediante una solicitud periódica a fin de obtener datos del servidor.

En está oportunidad vamos aplicar Comet con PHP y Prototype. Nos basaremos en un iframe oculto. El objetivo es que el cliente no pida al servidor, sino que el servidor envie datos al cliente.

El servidor nos retornará la hora UNIX

  • Necesitaremos un script en PHP que se encargará de la petición HTTP persistente (backend.php).
  • Un archivo HTML, donde irá el código JavaScript necesario y donde mostraremos los datos procedementes del servidor (index.html).
  • La librería Prototype (y un poquito de conocimiento de está librería).

El archivo backend.php

El script backend.php, hará un bucle infinito y devolverá la hora UNIX siempre y cuando el cliente esté conectado.

<?php
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
flush();
//flush -> vacia el búfer de salida de PHP y ademas envia todo lo escrito (echo, print, ect) al navegador del cliente

?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Comet php backend</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>

<script type="text/javascript">
//Navegadores KHTML no compartes javascripts entre iframes
var is_khtml = navigator.appName.match("Konqueror") || navigator.appVersion.match("KHTML");
if (is_khtml)
{
var prototypejs = document.createElement('script');
prototypejs.setAttribute('type','text/javascript');
prototypejs.setAttribute('src','prototype.js');
var head = document.getElementsByTagName('head');
head[0].appendChild(prototypejs);
}
// cargamos objeto comet (esto en realidad es el nombre del iframe)
var comet = window.parent.comet;
</script>

<?php
while(1) { // while (1) permite un bucle infinito
echo '<script type="text/javascript">';
echo 'comet.printServerTime('.time().');';
echo '</script>';
flush();
sleep(1); // un descanso para aliviar el CPU del servidor
}
?>
</body>
</html>

El archivo index.html

Este documento HTML cargará la librería Prototype dentro de las etiquetas <head>, también creamos dentro de <body> un contenedor donde se mostrará el resultado de la petición al servidor, ó sea la hora UNIX: <div id="content"> </div>, y finalmente se creará el objeto Comet que se conectará a nuestro archivo PHP.

El objeto Coment crea etiquetas <iframe> invisibles (esto quizás varie en algunos navegadores). Estos iframes se encargarán de crear el "terreno" (por así decirlo) para la conexión HTTP persistente al archivo/script PHP.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Comet demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="prototype.js"></script>
</head>
<body>
<div id="content">La hora del servidor se mostrará aqui</div>

<script type="text/javascript">
var comet = {
connection : false,
iframediv : false,

initialize: function() {
if (navigator.appVersion.indexOf("MSIE") != -1) {

// Para navegadores IE
comet.connection = new ActiveXObject("htmlfile");
comet.connection.open();
comet.connection.write("<html>");
comet.connection.write("<script>document.domain = '"+document.domain+"'");
comet.connection.write("</html>");
comet.connection.close();
comet.iframediv = comet.connection.createElement("div");
comet.connection.appendChild(comet.iframediv);
comet.connection.parentWindow.comet = comet;
comet.iframediv.innerHTML = "<iframe id='comet_iframe' src='./backend.php'></iframe>";

} else if (navigator.appVersion.indexOf("KHTML") != -1) {

// Para navegadores KHTML
comet.connection = document.createElement('iframe');
comet.connection.setAttribute('id', 'comet_iframe');
comet.connection.setAttribute('src', './backend.php');
with (comet.connection.style) {
position = "absolute";
left = top = "-100px";
height = width = "1px";
visibility = "hidden";
}
document.body.appendChild(comet.connection);

} else {

// Para otros navegadores (Firefox...)
comet.connection = document.createElement('iframe');
comet.connection.setAttribute('id', 'comet_iframe');
with (comet.connection.style) {
left = top = "-100px";
height = width = "1px";
visibility = "hidden";
display = 'none';
}
comet.iframediv = document.createElement('iframe');
comet.iframediv.setAttribute('src', './backend.php');
comet.connection.appendChild(comet.iframediv);
document.body.appendChild(comet.connection);

}
},
// esta función será llamada desde backend.php
printServerTime: function (time) {
$('content').innerHTML = time;
},
onUnload: function() {
if (comet.connection) {
comet.connection = false; // se eliminará el iframe para prevenir problemas con IE cuando se recargue la pagina.
}
}
}
Event.observe(window, "load", comet.initialize);
Event.observe(window, "unload", comet.onUnload);
</script>

</body>
</html>

En resumen, mediante JavaScript/Protype creamos en iframe donde aparecerá el contenido generado por backend.php, es este archivo el que mantendrá la conexión abierta con el servidor creando así un canal donde el servidor sea quien envie datos al cliente, en este caso la hora UNIX. Veremos luego un segundo ejemplo y seguiremos explicando al respecto.

8 COMENTARIOS

  1. Muy buen artículo! Aunque no entiendo por que para Firefox pones dos iframes y para Konqueror uno solo? Y por que usas ActiveX en IE? Podrías explicar eso un poquito más?

  2. Este código no es del todo funcional, para firefox y safari, debe o puede usarse XHR Streaming (ajax con estado readyState 3).

  3. @Emilio: eso que ves son diferentes hacks para evitar el “loading effect” en cada navegador. De esa forma se mama el browser y el puntero (así como la barra de carga) se ven normales y no como si la página todavía tiene algo para bajar.

    Saludos

  4. alguien me podria decir que hosting soporta comet? por favor, ya que he hecho una pequeña investigacion sin exito.

    Gracias

  5. No veo como utilizar esto por ejemplo en un chat, ya que ese backend.php tendria que estar leyendo un archivo cada vez? No es demasiado costoso para el servidor, incluso si se tuviera que leer en la base de datos?

  6. Entiendo lo que hace la técnica, pero si en el php, después del flush de todos modos tengo que realizar consultas a la base de datos cada cierto tiempo, entonces no veo donde esta el mayor rendimiento, un echo es que debo verificar de todos modos la base de datos….

Comments are closed.