Headers, PHP, Archivo zip e Internet Explorer

Para que no se me olvide porque me ha costado un montón de tiempo llegar a esta solución y por si a alguien le sirve:


if (strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) {
header("Content-type: application/zip");
} else {
header("Content-type: application/octet-stream");
}
header("Content-disposition: attachment; filename=\"documentos.zip\"");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0,pre-check=0");
header("Pragma: public"); // Sin esto el IE no funciona

// cargar el archivo a enviar
readfile($zipname);

Este es el código para enviar un archivo zip para su descarga. En mi caso, este script genera ese archivo .zip a partir de unos documentos y una vez generado, lo envía al navegador para su descarga.

Como siempre, todo funcionaba correctamente hasta que lo probé en Internet Explorer… Y tras mucho “goglear” y probar combinaciones, esta ha sido la que me ha funcionado en todos los navegadores.

Y además, aprovecho para escribir algo en el blog que con tanto Twitter y tanto Facebook está “abandonao” :)

Probando PHP 5.3.3

Como muchos ya sabréis PHP liberó ayer la versión 5.3.3. Entre otras mejoras, la que más me interesaba  y que llevaba esperando desde la 5.3 era:

– Added FastCGI Process Manager (FPM) SAPI.

En varios servidores estábamos usando PHP 5.3 con el parche de PHP-FPM, pero habíamos detectado varios errores que nos afectaban en la estabilidad del sistema, por lo que estábamos deseando pasar a la nueva versión.

Al enterarme de que el parche sería incluído en la rama oficial de PHP decidí no actualizar hasta que apareciera la versión estable que lo incluyera… y ese día fue ayer :)

Después de compilar (actualizando antes libevent) y comprobar que no había problemas de incompatibilidades con nuestro software, realicé varias pruebas con el Apache Benchmark (ab) y los resultados no están nada mal:
Document Path: /
Document Length: 6699 bytes

Concurrency Level: 10
Time taken for tests: 10.002 seconds
Complete requests: 499
Failed requests: 0
Write errors: 0
Total transferred: 3549886 bytes
HTML transferred: 3342801 bytes
Requests per second: 49.89 [#/sec] (mean)
Time per request: 200.446 [ms] (mean)
Time per request: 20.045 [ms] (mean, across all concurrent requests)
Transfer rate: 346.59 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 54 61 6.4 59 101
Processing: 126 138 9.8 137 206
Waiting: 66 75 9.1 73 146
Total: 181 199 10.5 198 273

Percentage of the requests served within a certain time (ms)
50% 198
66% 201
75% 204
80% 205
90% 208
95% 214
98% 232
99% 250
100% 273 (longest request)

En general el tiempo de respuesta ha bajado unos 600ms de media, lo cual no está nada nada mal :)

Ahora sólo nos queda tener suficiente tráfico como para que todo esto se note realmente :P

TYPOlight Interesante CMS Open Source

logoAcabo de encontrarme con TYPOlight y he quedado gratamente impresionado.

Se trata de un gestor de contenidos (CMS) open source desarrollado en PHP 5 que destaca por su flexibilidad y potencia, pero sobretodo por la importancia que dá a la accesibilidad y a la generación de código basado en los estándares W3C.

Podeis ver una demo o unas capturas de pantalla en su sitio web.

Redis, el ferrari de las bases de datos

redisRedis es una base de datos basada en clave/valor al estilo memcached, pero en la que sus datos son también guardados en disco, pudiendo usarla como reemplazo para MySQL y similares.

Está escrita en C y una de sus principales peculiaridades es su increíble velocidad, que en algunas pruebas le han llevado a marcar cifras como 110,000 operaciones SET  y  81,000 GETs por segundo en un ordenador básico con Linux.

Salvatore Sanfilippo, el creador de Redis, ha implementado un clon de Twitter para mostrar cómo se puede usar su base de datos desde PHP como reemplazo de MySQL y otras bases de datos y lo ha llamado Retwis. Según ha comentado, en breve escribirá un artículo para explicar al detalle el desarrollo de esta aplicación.

Redis (REmote DIctionary Server) , aunque aún en un estado muy prematuro de desarrollo, podría llegar a convertirse en alternativa para muchos proyectos que no necesitan de complejas estructuras de datos, pero si de cortos tiempos de respuesta ante elevadas cargas de tráfico.

Sin duda entran ganas de probarla. A mí se me ocurre más de un proyecto al que le vendría de perilla.

Vía Zend and the Art of Programming.

Usa las ETags en tu proyecto

Y no sólo porque lo diga Yahoo! sino porque te ahorrán tiempo y dinero. Harán que tu servidor sea capaz de procesar más peticiones y que estas sean más rápidas.

Últimamente y a medida que mis sitios web están ganando en visitas, estoy muy interesado en el tema de la optimización de los proyectos web y poco a poco, voy incorporando varias técnicas que incorporo en la fase final del desarrollo.Una de ellas es la de añadir ETag para los proyectos web que generan contenido dinámico.

La mayoría de los sitios web con contenido dinámico no están continuamente cambiando el contenido, sino que éste es modificado en momentos muy puntuales. Todo el tiempo entre un cambio y otro, el contenido es el mismo y sin embargo, hacemos que el servidor vuelva a generarlo y a enviarlo al servidor como si fuera completamente nuevo.

Las ETags son unos identificadores que se envían al navegador para identificar el “estado” de una página dinámica o de un archivo estático. Son fácilmente configurables en Apache para los archivos estáticos, pero pocas veces son usadas cuando el contenido es generado a partir de fuentes dinámicas.

Imaginemos el caso de este blog. Cada página es modificada únicamente cuando yo hago algún cambio desde el panel de control o cuando alguien deja/modifica un comentario. El resto del tiempo, el contenido de las páginas no varía.Podríamos por tanto identificar a cada página por su URL (p.ej.  “/2008/10/03/en-espana-se-vende-menos-que-se-compra/” ) y por la fecha en que fué actualizada. Si unimos ambas cosas, podríamos obtener un identificador único. Por ejemplo:

$etag = md5 ($_SERVER['REQUEST_URI'] . $last_modification_time);

Esa sería por lo tanto su Etag.  Y podríamos enviarla al navegador de la siguiente forma:

header("Etag: $etag");

El navegador, cada vez que pida esa página, enviará un header especial “If-None-Match” preguntando si es la misma página que ya le fué enviada la última vez. Con lo que antes de generar la página, podríamos comprobar:


if (trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) {
header("HTTP/1.1 304 Not Modified");
exit;
}

De esta forma, informaríamos al navegador de que la página no ha cambiado y por lo tanto no tendríamos necesidad de volver a generarla ni de enviarla por la red. Así ahorraríamos tiempo de proceso en el servidor (carga) y ancho de banda, además de ofrecer una mejor experiencia al usuario.

Por supuesto esta no es una solución válida para cualquier proyecto o situación ( en algunas puede ser incluso contraproducente ), pero si que supone una mejora en una gran parte de los desarrollos. Al menos así ha sido para mí ;)

En el Zend Framework también tenemos ya una propuesta para usarlas.

De página web a entrada en Twitter

El otro día estuve probando algunos de los componentes que el Zend Framework tiene aún en la incubadora o como simples propuestas. Entre ellos, encontré el Zend_Service_Twitter y se me ocurrió una idea.

Desde hace tiempo, sigo la web del equipo de Cárpatos en www.serenitymarkets.com y siempre he echado en falta el que dispusiera de un feed para poder seguirlo con mi agregador. Pero además, viendo la frecuencia de actualización, se me ocurrió pensar que es prácticamente lo mismo que lo que ocurre en una cuenta Twitter, donde se va contando minuto a minuto lo que va pasando en los mercados financieros.

Gracias al ZF, pasé del dicho al hecho en poco más de 1 hora. Convertí el html de su web en un documento DOM y con ese documento, creé un Feed (Zend_Feed_Builder) y con el Zend_Service_Twitter hice que cada vez que aparecía una entrada nueva, esta se publicara en una cuenta de Twitter ( twitter.com/carpatos ).

No todo puede ser trabajo, de vez en cuando toca divertirse un ratito :)

Hay mucha gente que defiende que en PHP se pueden crear grandes aplicaciones sin necesidad de Frameworks. Yo estoy con ellos, creo que los frameworks no son necesarios, pero si son útiles. En algunos casos, muy útiles, ya que te ayudan a realizar tu trabajo en mucho menos tiempo y a realizar tareas muy complejas con unas pocas líneas de código.

Actualización: En este momento he tenido que cerrarlo por algunas razones que ya revelaré más adelante ;)