Detectar en PHP si un gif es animado

La siguiente función sirve para detectar si un GIF es animado o no (tiene más de 1 frame).

  function isAnimatedGif($filename)
  {
    $filecontents=file_get_contents($filename);

    $str_loc=0;
    $count=0;

    # There is no point in continuing after we find a 2nd frame
    while ($count < 2)
    {
      $where1=strpos($filecontents,"\x00\x21\xF9\x04", $str_loc);
      if ($where1 === FALSE) {
        break;
      }

      $str_loc = $where1+1;
      $where2  = strpos($filecontents,"\x00\x2C",$str_loc);
      if ($where2 === FALSE) {
        break;
      }
      else {
        if ($where1+8 == $where2) {
          $count++;
        }
        $str_loc = $where2+1;
      }
    }

    // gif is animated when it has two or more frames
    return ($count >= 2);
  }

También está la siguiente alternativa, más compacta que la anterior:
<pre>
function isAnimatedGif($filename) {
return (bool)preg_match('#(\x00\x21\xF9\x04.{4}\x00\x2C.*){2,}#s', file_get_contents($filename));
}

El código lo he modificado minimamente a partir de lo que he encontrado en los comentarios de http://es2.php.net/manual/en/function.imagecreatefromgif.php

, ,

Instalar impresora Canon PIXMA iP2600 en Ubuntu 9.04 64-bits

Canon PIXMA iP2600
Durante mi estancia en inglaterra compré una impresora Canon PIXMA iP2600. Salía bien de precio y los cartuchos de tinta, aunque caros, eran los más baratos de todos los que estube mirando.

El caso es que ¡oh sorpresa! resulta que la impresora no funcionaba en Ubuntu 64-bits, y tras leer mucho y probar varias cosas, no habia manera.

Resulta que ayer actualicé ubuntu, y se actualizaron algunas cosas relacionadas con la impresora, así que me dije, volvamos a intentarlo, y hoy ha funcionado ya esta impresora a la primera.

El tema, es que los drivers de Canon para la PIXMA iP2600 sólo estan en 32-bits, y mi ordenador es 64-bits, así que hay que hacer un truco manso (truco que no funcionó hace 2 o 3 meses cuando intenté instalarla).

Así que, para el que le pueda interesar, los pasos que he seguido para instalar esta impres han sido:

  • Ir a la página de Linux Printing y buscar mi impresora
  • He descargado los drivers para 32-bits (no existen los de 64-bits):
  • He forzado la instalación de ambos paquetes con dpkg:
    • sudo dpkg -i –force-architecture cnijfilter-common_2.90-1_i386.deb
    • sudo dpkg -i –force-architecture cnijfilter-ip2600series_2.90-1_i386.deb
  • Y finalmente he conectado el USB, he abierto el Printer Manager (System -> Administration -> Printing), y le he dado a nueva impresora, después he pulsado siguiente  siguiente dos veces, le he dado a finalizar, me ha preguntado si imprimía una página de prueba y…
  • ¡voila! la página se ha imprimido con éxito!

, , ,

Deployment continuo: el caso práctico

Por ver un poco cual sería el caso práctico de un sistema automático y continuo para hacer deployments (o despliegues), voy a compartir lo que estuve pensando el otro día.

Antes de nada debo decir que sigo sin haberlo implementado, así que hablo de ideas que tengo en mente, y no de un caso real.

Aunque en el artículo anterior hacía una introducción más detallada al tema de deployment/despliegue continuo, vuelvo a repetir el concepto:

El concepto en plan radical, es que cada vez que se haga un cambio en el código, automáticamente tu servidor de producción se actualice.

Esto es un poco hardcore, lo sé, porque muchas veces se introducen bugs, y se molesta a los usuarios innecesariamente, así que este sistema debe ser lo suficientemente inteligente para tratar de evitar esto en la mayor medida posible. Es mejor post-poner un par de dias un release, que molestar a cientos de usuarios con un bug.

Una vez dicho esto, y partiendo de la base de que ya se tiene un SCV (sistema de control de versiones), un sistema de build y un sistema de tests (tests unitarios, de integración y tests de casos de uso sobre la web, por ejemplo con selenium), el procedimiento para hacer despliegues continuos podría ser el siguiente:

  • Dado un nuevo build
    • Si los entregables no se han generado correctamente, fin de la historia, no hay deployment posible
    • Si algún test (test unitario, de integración, de casos de uso, o de lo que sea) ha fallado o no se ha podido realizar, no hay más que hablar, tampoco hay deployment
  • Si el build está bien, entonces se crea un backup de todo el servidor de producción (bases de datos, scripts, etc…)
    (La idea de este paso, como se verá luego, no es tanto hacer un backup real, como tener una replica similar a producción)

    • Si hubiera algún fallo generando los backups, nuevamente fin de la historia
  • Una vez se tiene el backup, hay que descargarlo y verificar que funciona. Esto significa, idealmente, hacer un clon de producción, es decir:
    • Descargar el backup que hemos creado en una máquina virtual “limpia”
    • Instalar el backup (tanto código como datos)
    • Pasar un subconjunto de tests, lo suficientemente grande para saber que la instalación ha funcionado, y lo suficientemente pequeño para que este paso no tarde demasiado (quizá algunos tests unitarios y varios casos de uso)
  • En este punto, debemos usar el último build que hemos obtenido en el primer paso y…:
    • actualizar el clon de producción
    • pasar nuevamente todos los tests de este nuevo build sobre ese clon de producción y ver que todo funciona bien (si algo falla, el nuevo build no se podrá desplegar y tendremos que mirar que ha ocurrido). Estos tests, deberían incluir:
      • tests unitarios
      • tests de integración
      • tests de consistencia de tablas (ver que la base de datos resultantes es la que tiene que ser)
      • tests de consistencia de datos (ver que no nos hemos pulido datos que estaban antes en producción)

Llegados a este punto, si todo ha funcionado,  sabemos que nuestro build puede instalarse por encima de un clon de producción y pasar TODOS los tests, es decir, que si el propio proceso de clonar producción funciona, repetir este proceso en producción debería nuevamente ser determinista y darnos el mismo resultado (usease, que nuestro build funcione y no cause problemas en producción).

Así pues, los pasos a realizar ahora por nuestro sistema automático de deployment, serían:

  • Subir el build a producción (de hecho, este paso se puede ir haciendo en paralelo a los pasos anteriores)
    • Este paso incluye descomprimir el build y prepararlo (si es que hay que preparar algo), pero sin tocar nada que pueda afectar a producción aún
  • Poner producción en modo “estamos actualizando, disculpen las molestias”
  • Hacer el backup real de las bases de datos y/o cualquier archivo que pueda ser susceptible de ser cambiado (idealmente backup de todo)
  • Ejecutar el script de auto-upgrade que tengamos, y actualizar tanto las bases de datos como el sistema de ficheros (tal cual se ha hecho en el clon de producción)
  • Desactivar el mensaje de “estamos actualizando”
  • Mandar un e-mail a quien corresponda avisando de que el sitio ha sido actualizado ;)
  • Y finalmente lanzar los tests para verificar que efectivamente todo funciona bien
    • Si por un casual alguno de estos tests falla, es que algo en nuestro sistema de clonado de producción/autodeployment está podrido, y nos dejará bien  jodidos. Nótese que tener un fallo en este punto es igual de chungo que haber hecho un release como los de toda la vida y ver que algo ha fallado, indicaría que algo en nuestro sistema de build/test está igualmente podrido… Este tipo de fallos no debería de ser normal.

Nótese que si algo falla en estos pasos (sobre producción), el sistema debería ser capaz de reestablecerse con el último backup realizado.

Después de haber comentado todo esto, también hay que tener en cuenta, que algunos pasos dependen mucho del tipo de sitio que se tiene, porque no es lo mismo hacer un backup de 50 megas, que de 5 teras, o de 2 peta bytes.  Si el volumen de información es muy grande, pues habrá que depender de los otros sistemas de backup que se dispongan, y usarlos convenientemente, con el fin de minimizar los tiempos de deployment.

En este tipo de casos, tampoco se podrá realizar un clon de producción para validar que el upgrade funciona, pero en vez de eso se puede coger sólo un subconjunto de los datos de producción (por ejemplo 1000 entradas, o 1 millón, yo que se). El tema es validar que todo funciona antes de subirlo.

Imagino que también cambia mucho el tema de si se trata de un servidor único, o son varios servidores. En el caso de que sean varios los servidores a los que afecta el  nuevo release, es posible que sea mejor introducir el nuevo build en uno de ellos, y si después de X tiempo no hay incidencias, que se propage a los demás.

Otra cosa que me gustaría comentar antes de finalizar el artículo, es que no quiero que se me malinterprete. En un caso práctico no tiene sentido hacer actualizaciones en el servidor para cada submit que se hace en el sistema de control de versiones.  Entre otras cosas, porque tampoco todos los tests pueden ser automáticos, a veces combiene hacer tests manuales. Así que probablemente lo que realmente tiene sentido y valor, es que para cada build se sepa si puede ser instalado en producción sin problemas o no (y para eso poder generar un pseudo-clon de producción es esencial), así como disponer de un botón que te haga deployments automáticos. Incluso puede tener sentido, que el sistema haga los deployments una vez al día o cada dos días, o incluso cada semana, durante las horas de menos visitas.

Para finalizar, debería quedar claro que cada cual debe adaptar estas ideas a sus recursos (sobretodo de tiempo) y sus necesidades, y sobretodo que los sistemas de despliegue automático y contínuo se pueden (y probablemente se deben) ir construyendo poco a poco.

Ale, pues nada más, espero vuestras críticas y comentarios, si es que a alguien le quedan fuerzas después de haber leido esta entrada ;)

PD: puff, creo que voy a tardar un par de días en traducir esto a inglés… madre de Dios, que tocho que me ha quedado

, ,

Deployment continuo

Hace algún tiempo leí en el blog de Eric Ries acereca de como su empresa hace deployment continuos.

Deployment es una palabra que no me atrevo a traducir, porque la he usado tanto en inglés, que ninguna de las traducciones que se me ocurren me suena bien (acepto sugerencias).

Deployment es el proceso por el cual se saca el nuevo producto. Por ejemplo, si tu tienes subida una web al servidor y actualizas a la última versión, esa actualización sería el deployment.

Digamos que el deployment continuo, es el siguiente paso lógico tras implantar un sistema de builds continuos. Es decir, partiendo de un build válido (que ha sido generado correctamente y ha pasado todos los tests), el siguiente paso asumiendo que todo ha funcionado bien, sería subir los cambios al servidor para que todo el mundo pueda disfrutar de los cambios que hemos realizado.

Por supuesto, antes de actualizar el servidor, tenemos que estar seguros de que todo va a funcionar bien, no queremos subir algo que cause o pueda causar molestias a los usuarios, por eso va a ser importante disponer de todo tipo de tests que validen lo que hemos hecho.

Las ventajas de subir los cambios continuamente son varias, pero yo destaco estas tres:

  • los usuarios de tu sitio web van a disfrutar antes de las últimas mejoras y nuevas funcionalidades
  • vas a tener feedback de tus usuarios al poco de haber implementado las cosas, y no 3 meses más tarde, con lo que podrás seguir mejorando o corrigiendo las cosas
  • tendrás un proceso automatizado de actualizar el servidor, con lo que ahorrarás tiempo a largo plazo

Entre las desventajas más importantes, yo destacaría que hacer todo esto tiene un coste y un tiempo asociados, aunque lo he estado pensando y no es tan elevado como uno pueda imaginar, si se están haciendo las cosas bien.

Seguramente me dejo cosas en el tintero, tanto en ventajas como en desventajas, pero el concepto me parece excelente, y creo que hoy día puede marcar la diferencia entre unas webs y otras.

Finalmente me gustaría comentar que este post ha surgido porque he estado pensando en como implementar esto para el curso de mecanografía y automatizar así los deployments. De momento aún no lo tengo, pero ya he pensado como podría hacerlo, y seguramente en el siguiente post comparta mis ideas sobre el caso práctico, a ver que os parece.

Enlaces muy recomendables:

Otros enlaces:

, , ,

phpmyadmin root access denied

Resulta que el jueves me actualicé al nuevo Ubuntu 9.04, y como no, siempre hay algún que otro problemilla. Hoy me he encontrado que PHPMyAdmin no me dejaba loguear con el usuario root en mi propia máquina de desarrollo O_o

Buscando y buscando a ver por qué podia estar pasando esto (y tras verificar desde la consola que el usuario root sí podia loguearse en MySQL), he encontrado la solución:

En el archivo /var/www/phpmyadmin/libraries/config.default.php, linea 345 en mi configuración:
$cfg['Servers'][$i]['AllowNoPasswordRoot'] = false;

Símplemente cambiando esa linea por la siguiente, se soluciona el tema:
$cfg['Servers'][$i]['AllowNoPasswordRoot'] = true;

Espero que le pueda resultar útil a alguien más ;)

, ,

Menus gratis para tu web

Me he encontrado con una serie de diseños de menus  y creo que es buena idea compartirlos, no sólo porque muchos son bonitos, si no porque las cosas gratis siempre vienen bien, sobretodo en estos tiempos de crisis que corren ;p

menus buenos bonitos y baratos

Estos menus han sido diseñados por Ian Main de e-lusion.com, podeis echarle un vistazo a la página de menus gratuitos, de donde podreis descargarlos.

, , , ,

Oferta de trabajo para programadores C++

Real Networks Logo Tal y como está el patio, creo que esta oferta le va a interesar a más de uno, claro que primero tiene que haberse tropezado con mi blog ;)

Si te apasiona programar, sabes usar un debugger, sabes como funciona y sabes como está estructurado un sistema operativo, sabes lo que es un PE sin tener que hacer click en el enlace, y quieres pasartelo bien a la vez que trabajas y te pagan, te recomiendo que le heches un vistazo a la siguiente oferta, porque te va a gustar.

En esta oferta de trabajo que os adjunto vereis que se está buscando básicamente a un programador de C/C++ de la vieja escuela. Con amplios conocimientos de programación, desde ensamblador hasta javascript y sólo dios sabe qué mas; donde lo que va a predominar es C/C++ (esto lo digo con conocimiento de causa). Creo que es una oportunidad cojonuda para el que quiera crecer como programador, porque se trata de un trabajo donde hay que estar continuamente innovando. Por cierto, que el nivel de inglés debe ser decente (aunque si no es muy bueno, y consigues el trabajo, estoy convencido que mejorará).

La empresa madre es Real Networks, está en El Campello (Alicante), justo al lado de la playa y además desde la oficina hay vistas al mar ;)

Hace ya casi 6 meses que no formo parte del equipo, pero después de haber trabajado 4 años y medio en esta empresa, debo decir que ha sido un trabajo que me ha hecho crecer personal y profesionalmente.

Otras cosas que puedo decir de la empresa, y aviso que puedo estar metiendo la pata ahora mismo, es que al menos cuando yo trabajaba (hace 6 meses) habían clases de inglés para el que las quisiera (y nos lo pasábamos bastante bien, y eso que yo siempre he odiado el inglés); que la empresa hacía una compra semanal en el mercadona (esto ya no se si sigue o no, pero estubimos como 2 años comprando doughnuts, bizcochos, galletas, frutas varias, coca cola, etc…).

Finalmente, el ambiente y la gente que hay en la empresa es, y siempre ha sido, cojonudo ;)

, , ,

Escalar una imagen manteniendo el aspecto

Muchas veces es necesario escalar una imagen para que se mantenga dentro de unos límites y pueda visualizarse correctamente. Es mucho mejor si la imagen no se deforma. En estos casos hay que hacer un escalado manteniendo el aspect ratio.

Hoy día hay cientos de librerias para cualquier lenguaje que realizan un escalado. La única pregunta es, ¿cuales deben ser el nuevo ancho y largo de la imagen?

A continuación el código mágico que está probado en javascript, pero que debe funcionar en PHP, python, java, perl, C, y lo que le pongas por delate :)

if (w < h) {
  h = (h * maxWidth) / w;
  w = maxWidth;
}
else {
  w = (w * maxHeight) / h;
  h = maxHeight;
}

w representa el ancho o width de la imagen

h representa el alto o height de la imagen

maxWidth la anchura máxima del área en la que se quiere meter la imagen

maxHeight la altura máxima del área en la que se quiere meter la imagen

Como ejemplo final, aunque es prácticamente copy paste, imaginad que quereis meter una imágen de tamaño (w, h) en un área de 640×480:

var maxWidth = 640, maxHeight = 480;
if (w > maxWidth || h > maxHeight) {
  if (w < h) {
    h = (h * maxWidth) / w;
    w = maxWidth;
  }
  else {
    w = (w * maxHeight) / h;
    h = maxHeight;
  }
}

, , , , ,

Pensamientos sobre diseño y usabilidad

Mi capacidad de abstracción conceptual es, creo, bastante decente, sin embargo mi capacidad de diseño, por qué no decirlo, está atrofiada.

Por una parte creo que soy pésimo diseñando webs (a nivel gráfico), sin embargo creo que me estoy volviendo bastante bueno en cuanto a usabilidad se refiere (mis horas me está costando).

Conseguir un interfaz que sea agradable a la vista e intuitivo en cuanto a usabilidad no suele ser fácil. Sobretodo porque muchas veces nuestra perspectiva está limitada, vivimos en nuestro mundo y creemos, de forma inconsciente, que los demàs piensan y razonan como nosotros. Obviamente esto no es así ;)

El tema de usabilidad, al igual que el diseño,  puede ser considerado un arte. Si bien no hay recetas mágicas que nos digan como hacer que un determinado diseño sea bonito y usable, si que hay ideas y conceptos que nos pueden ayudar.

El libro de Don’t Make Me Think es un clásico, y es de lectura obligada, tanto para diseñadores como para programadores. El concepto que se me ha quedado del libro (varios años depués de haberlo leido) es el mismo que reza su título, no me hagas pensar. Si quieres que un usuario se sienta agusto en tu página web, no lo agobies con muchos enlaces, no pongas menús con 200 acciones posibles, si el usuario quiere hacer algo que lo encuentre fácil y lo pueda hacer en par de clicks. Cuantos menos clicks tenga que hacer y cuanto menos le hagamos pensar mucho mejor. Que la navegación por la página sea como un acto reflejo :)

Por otra parte, hace tiempo empecé a leer otro libro, que por cierto no he terminado, titulado The design of everyday things. Este otro libro, aunque está pensado y escrito desde la perspectiva de alguien que está diseñando cosas tangibles, cosas para el mundo real, es otro libro muy muy recomendable. Después de leer varios capítulos de este libro se me abrió un poco más la mente. Ahora soy consciente de que uno tiene que diseñar cosas de tal manera que las personas que las usen ni siquiera se den cuenta de que están ahi.

Seguramente más de uno se preguntará, por qué diseñar algo para que luego el usuario ni siquiera se de cuenta. La razón es muy sencilla, viene de la parte de usabilidad de un diseño, es muy fácil darse cuenta cuando algo está mal, cuando algo cuesta, pero si va bien uno ni se entera.

Por poner un ejemplo del mundo real, ¿no os ha pasado alguna vez, que intentais usar un determinado aparato o utensilio y siempre empezais a hacerlo mal? Ya no digo que no sea intuitivo, si no que sabiendo que se hace de la forma X vosotros lo empezais a hacer de la forma Y, y cuando os dais cuenta rectificais.

Pensad en el siguiente ejemplo superevidente de fallo garrafal. Imaginad que teneis un bote de mermelada que quereis abrir, y resulta que os poneis a abrir, a abrir, haceis fuerza y no hay manera. No se abre. Cogeis un paño de cocina por encima de la rosca, le dais le dais, nada. Cogeis un cuchillo, le dais golpecitos. Nada, que no se abre. ¿Por qué no se abre esta maldita mermelada? Resulta que la marca hace las roscas al revés, por lo tanto el sentido intuitivo que habeis desarrollado para abrir el bote, resulta que  lo cierra, mientras que cerrarlo lo abre.  Seguro que no volveis a comprar esa marca. Lo lógico es que las roscas, al igual que los tornillos, giren todas en el mismo sentido para evitar problemas de usabilidad.

Ahora un fallo no tan evidente que habreis observado más de una vez, si no en vuestra propia casa, en la de algún amigo/a (si no lo habeis observado probablemente no lo entendereis). No os ha pasado querer enchufar una determinada luz de la casa (la luz de un pasillo, del salón, etc…), y  resulta que encendeis otra. Y resulta que llevais años viviendo en la casa, y/o yendo a esa casa, y casi siempre os equivocais.  Pues bien, os hago saber que no es culpa vuestra. Vereis que le pasa también a más gente al utilizar esos interruptores. Normalmente, suele ocurrir en interruptores que controlan dos luces, y resulta que la luz más cercana a la puerta/habitación es la que enciende la luz más alejada (esto es lo que yo he observado al menos). Lección que podemos aprender: si un usuario mete la pata una y otra vez, a pesar de que es consciente y sabe como se tiene que hacer, el diseño falla.

Por cambiar el chip y dar un ejemplo de un buen diseño, siguiendo con el mundo real,  un gran acierto en diseño que me viene a la cabeza ahora mismo, son los muebles de IKEA. Dios mio, ¡¡¡que no tienen instrucciones!!! ¡¡¡Son 5 dibujitos!! Probablemente muchos de los muebles se puedan montar sin siquiera mirar instrucciones. ¿Por qué? A parte de que los dibujos de las instrucciones son muy sencillitos,  creo que la razón principal es que si  lo intentas montar mal, ¡las piezas no encajan! En los muebles de IKEA suele haber muy pocas combinaciones para montar algo. Sinceramente, dudo mucho que la gente de IKEA deje al azar el tema de que tornillos usar para encajar las distintas piezas en cada uno de sus muebles. Esta gente sabe lo que se hace. Sabe de usabilidad.

Hasta donde yo soy consciente hoy día, los aciertos en diseño son muy dificiles de detectar, y sin embargo detectar cuando algo le falla a un diseño es algo bastante fácil. Si sientes, aunque no sepas el motivo, que a un diseño le falla algo, es porque con un 99% de certeza, le falla algo.

El problema con los diseños, como he dicho al principio, es que son un arte, y tu puedes saber que está fallando X, pero señores, no hay receta para saber como arreglarlo. Depende de cada situación (imagino que habrá patrones de diseño para evitar errores comunes en determinadas areas).

Finalmente, recordad esto: si el usuario tiene que pararse a buscar que es lo siguiente que tiene que hacer, si tiene demasiadas opciones, si no encuentra lo que busca, si tiene que hacer varios clicks o navegar por varias páginas hasta llegar a donde quiere, o si el usuario siempre comete algún tipo de error una y otra vez, entonces el diseño, amigos mios, es mejorable.

, ,

Prevenir ataques XSS con HTML Purifier

HTML Purifier

Que son los ataques XSS (cross site scripting) y por qué hay que evitarlos es algo esencial que merece un post a parte.

La idea con la que hay que tienes que quedarte, es que si tienes o planeas realizar una página web donde permites que otros usuarios editen o publiquen contenido HTML (ya sea en forma de páginas, comentarios, etc…), debes de alguna manera validar que el contenido HTML que están generando es seguro.

Hay muchas librerias que se encargan de filtrar y validar estos contenidos HTML para asegurarse que la gente que sube contenidos no va a hacer nada malo. Yo por mi parte he estado mirando hace un rato, porque tampoco iba a ponerme a hacer la mía (no es mi objetivo en la vida  sacrificar dos semanas de trabajo si puedo usar algo que ya existe y funciona), y me he encontrado con HTML Purifier, una librería de PHP bastante extensa y con muchisima funcionalidad que se encarga de filtrar codigo maligno.

Utilizarla es muy muy sencillo. Sólo requiere dos lineas (una vez incluido el archivo base).

require_once '/path/to/htmlpurifier/library/HTMLPurifier.auto.php';

$purifier = new HTMLPurifier();
$clean_html = $purifier-&gt;purify($dirty_html);

Lo bueno de esta libreria, es que se pueden configurar muchisimas cosas, como limitar el número de tags, los atributos por tag, etc… e incluso es capaz de reformatear código html inválido y convertirlo en código válido (tal y como pueden hacen otras librerias como BeautifulSoup en Python o como Tidy HTML para C/C++)

También cabe destacar que en principio esta libreria NO REFORMATEA el código HTML, simplemente se encarga de eliminar o reformatear las partes peligrosas o inválidas.

Seguramente en el futuro vuelva a hacer algún otro post sobre el tema, sin embargo, si teneis ocasion, recomiendo echarle un vistazo al libro Building scalable web sites de Cal Henderson. Aunque seguramente en la wikipedia y googleando un poco encontrareis bastante información.

, , ,

prev posts