Cosas que no hay que hacer al redireccionar una página

En estos últimos tiempos he aprendido dos cosas que no hay que hacer nunca cuando quieres redireccionar una página si no quieres hundirte en la miseria.

Cosa 1: Redirecciones 301 a 404

Uno de los fallos más habituales que comentemos cuando hacemos una redirección 301 es que solemos mandar tooodo a la nueva URL, dominio o lo que toque en cada caso… pero no solemos parar a mirar si una página se está redireccionando hacia una que da error, o sea, una que devuelve un código 4xx.

¿Y qué pasa con los buscadores? Pues que como siempre se lían con los 3xx, 4xx y 5xx, por norma general, si haces una redirección a una página de error, esas páginas se acaban añadiendo al índice secundario y se monta un poyo bastante hermoso.

La cuestión es que, los casos en los que una redirección 301 acaba mandando a una página 404, esa página, puede que visiblemente no esté en los resultados de búsqueda, pero si revisas en Webmaster Tools aparecen “cosas raras”.

Cosa 2: Canonical con noindex

Una de las formas más sencillas de hacer una redirección, o mejor dicho, de corregir si una dirección URL no es correcta, es la de usar la meta-etiqueta canonical. Con esta etiqueta, básicamente le decimos a los buscadores que “ignoren” la direción URL que se encuentra en la ventana del navegador y que la que han de poner e indexar es la que aparece en el código fuente. De esta forma, si algún juanker quiere hacer cosas malignas, no va a poder conseguirlas.

El problema más grande es que en algunas ocasiones, uno se puede plantear que “la página actual” ha de ser otra (por eso ponemos el canonical) y no queremos que la “vieja” se indexe (por lo que ponemos el noindex)… ¿qué ocurre? Que tanto la nueva como la vieja no se indexarán al tener ese noindex.

Teniendo en cuenta que el canonical lo que acaba haciendo es “eliminar” la dirección URL antigua, no hace falta utilizar el noindex, ya que perjudica en la aparición en los resultados de búsqueda.

Un par de trucos para WordPress con .htaccess

Muchas veces queremos hacer cosas en WordPress y buscamos plugins que pueden sobrecargar el sistema de forma absurda, pudiendo hacer mejoras gracias a unas pocas líneas del .htaccess.

Reducir spam en comentarios

En muchas ocasiones los robots de spam están tan mal hechos que no incluyen ningún tipo de referrer, algo que los usuarios por norma general sí que permiten… así que:

RewriteCond %{REQUEST_METHOD} POST
RewriteCond %{REQUEST_URI} .wp-comments-post\.php*
RewriteCond %{HTTP_REFERER} !.*dominio.com.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^$
RewriteRule (.*) ^http://%{REMOTE_ADDR}/$ [R=301,L]

Evitar enlaces multimedia externos (hotlinking)

Gracias a esto puedes impedir que los sitios que te enlacen y usen tus imágenes reciban un bonito mensaje…

RewriteCond %{HTTP_REFERER} !^http://(.+\.)?dominio\.com/ [NC]
RewriteCond %{HTTP_REFERER} !^$
RewriteRule .*\.(jpe?g|gif|png)$ nohotlinking.png [L]

WTF – Web Testing Framework

Muchos sabéis que una de las herramientas que últimamente tengo bastante presente es YSlow, y hace unos días que descubrí una ampliación de esta herramienta llamada WTF (Web Testing Framework) que simplemente revisa algunos elementos del código de una página web.

Entre los elementos que revisa están:

  • Si se usa el elemento <blink>
  • Si se usa el elemento <marquee>
  • Si se usa el elemento <font>
  • Si no existe el <doctype …>
  • Si se usan imágenes GIF para ajustar el diseño
  • Si se usan enlaces con # o javascript

La versión 0.0.1 se lanzó el pasado día 20 y para utilizarla sólo hace falta que ya esté en funcionamiento la herramienta de Yahoo!. A ver si su creador le va añadiendo más elementos que no contempla la herramienta “padre”.

HTML 5: los “malditos” iframes

Aunque es uno de los elementos que se utilizan todavía, la verdad es que el consumo que provoca en los navegadores es tan alto que si nos paramos a pensar fríamente, no es nada recomendable su uso… aún así, el HTML 5 todavía lo mantiene ya que para algunas cosas sigue siento un elemento valioso (aunque, como digo, yo no lo recomiendo, ni yo ni Google ni Yahoo!…)

iframe

El elemento iframe básicamente permite integrar lo que se podría decir como una ventana de navegador dentro de otra, o sea, una página dentro de otra, en un espacio definido. De esta forma podemos abrir el contenido de una página propia o externa dentro de una parte de la que actualmente visualizamos.

El principal atributo que lo acompaña es el src que como en muchos otros elementos, hace referencia a una dirección URL, que será la página que abrirá dentro. También se le puede indicar el srcdoc que hace referencia al nivel de profundidad que tendrá el elemento. El atributo name hará referencia al nombre del elemento (por si hay que acceder a él a través del DOM).

Uno de los mayores problemas que tienen los iframes es la seguridad que puede comportar abrir “cualquier” elemento externo a nuestro sitio. Es por esto que existe el atributo sandbox (al que por ejemplo ya da soporte Google Chrome) y que restringe algunas acciones. Algunos de los valores que puede generar excepciones son:

  • allow-same-origin
  • allow-top-navigation
  • allow-forms
  • allow-scripts

<iframe sandbox="allow-same-origin allow-forms allow-scripts" src="http://www.example.com/incrustado.html"></iframe>

Otro atributo que parece muy interesante es seamless. Con este atributo le decimos al navegador que “renderice” el contenido del iframe como si fuera parte del sitio. Esto implicará que haya algunas restricciones y ampliaciones, como que el contenido ha de estar en el mismo dominio, que los CSS que haya en el iframe pasarán a formar parte del CSS principal…

Para acabar, y como es lógico, hemos de indicar el tamaño de la ventana, y para ello utilizaremos los atributos width y height, que indicarán el ancho y alto de la ventana.

Sin duda el elemento iframe va a ser uno de los más criticados por los buscadores y optimizadores de la red, pero parece que va a seguir aquí durante bastante tiempo.

Web Performance Optimization, básico en un negocio web

Según voy adelantando en esto de mejorar los sitios web, ya no por SEO únicamente, sino pensando en los usuarios, me doy cuenta de la importancia esa de frases como: es que Google va muy rápido. Sí, la verdad es que sí, se agradece muchísimo que un sitio vaya muy muy rápido hoy en día, porque en gran medida es lo que hace que el usuario se sienta a gusto, porque puede navegar casi instantáneamente.

Uno de los inversores importantes en Estados Unidos, Fred Wilson, que ha invertido en Twitter, delicious, Etsy o FeedBurner cuando habla del TOP 10 de las aplicaciones web, pone en el número uno la velocidad del sitio: First and foremost, we believe that speed is more than a feature. Speed is the most important feature. If your application is slow, people won’t use it…; así que vale la pena siempre darle una ojeada a la optimización del sitio, ya sea a nivel de SEO, a nivel Usabilidad, o a nivel de que el código sea 100% compatible, pero normalmente no nos miramos con tanto ojo si el servidor web, la base de datos o el lenguaje de programación están configurados de forma óptima.

Conseguir algo como esto no es nada sencillo, sobretodo si vienes de un sitio bastante complejo y con muchos problemas:

Pero conseguir algo como esto en tan sólo unos días, creo que es bastante emocionante…

Y es que dentro de poco creo que vamos a ver nacer un nuevo tipo de trabajo, gente que se dedicará principalmente a hacer mejorar el rendimiento de los sitios web, un trabajo que muchos administradores de sistemas deben tener presente, ya que son ellos los principales beneficiarios de algo así.

Un googlero llamado Steve Souders, anteriormente trabajador en Yahoo! y conocido como Yahoo! Superstar, además de ser el creador de una de las herramientas básicas, el YSlow, publicó una entrada hace cosa de un mes en la que explica algunos temas interesantes:

  • Rapidez por defecto: muchas aplicaciones que se construyen para CMS, lenguajes de programación, “la nube”, bibliotecas de JavaScript, navegadores, servidores… ya están pensadas para ir rápido.
  • Maquetación del navegador: con el fin de hacer que las páginas web más rápido los desarrolladores necesitan la capacidad de encontrar qué partes son más lentas. Esto requiere revisar el tiempo que tarda en cargar y ejecutarse el JavaScript, los CSS, la maquetación de los elementos, la gestión del DOM…
  • Consolidación: las herramientas de rendimiento de la web, servicios y similares no han llevado un único camino, sino que cada uno ha puesto sus esfuerzos de forma separada. Eso va a cambiar y pronto veremos herramientas que combinan la depuración de JavaScript, el perfil de JavaScript, DOM, el uso de la red… todo en una sola herramienta. Las métricas de rendimiento se gestionarán desde un único panel en lugar de tener que visitar múltiples servicios separados. La consolidación también va a ocurrir a nivel de empresa, donde las empresas más pequeñas relacionados con el rendimiento son adquiridos por las grandes empresas de consultoría y servicios.
  • TCP y HTTP: Los protocolos por los que funciona Internet deben ser optimizados, y SPDY es una propuesta. Tenemos que tratar de conseguir más apoyo para el pipelining. Cualquier mejora en la red llegará a todos los sitios y el usuarios.
  • Estándar: hay que establecer un estándar sobre las formas de medir, los datos, las pruebas… La Web Timing Spec es un primer ejemplo a tener presente.
  • Organizaciones en la industria: dentro del mundillo de la WPO veremos nacer y crecer organizaciones profesionales, formación, certificaciones, organismos de normalización… Un ejemplo podría ser que los editores web compartan información acerca de los anuncios de publicidad lentos.
  • Los datos: hacer seguimiento de los resultados y encontrar nuevas oportunidades de rendimiento requiere un gran análisis de datos. Es probable que comiencen a verse repositorios públicos de datos relacionados con el rendimiento.
  • Verde: los estudios realizados que cuantifican cómo mejorar el funcionamiento web confirman la reducción del consumo de energía y por ello la contaminación que generan los centros de datos.
  • Rendimiento móvil: es como un nuevo punto de partida, se necesita recopilar todo tipo de información hasta encontrar los principales problemas, las causas y encontrar soluciones y crear herramientas para así poder ofrecer información sobre todo esto.
  • La velocidad como elemento diferenciador: muchas de las decisiones que se tomarán sobre Internet se basarán en el rendimiento. Cuando alguien adquiera un dispositivo, elija un proveedor, se revise un sitio web, la lealtad de los usuarios será un factor importante a la hora de hacer mediciones.

En fin, tengo claro que para los usuarios, para las conexiones a Internet, para los buscadores… cuanto mejor vaya un sitio web, cuanto más óptimo sea, si cabe, mejores resultados van a obtenerse a medio plazo.

Dominios sin cookies

Una de las cosas de las que últimamente se habla bastante es de los CDN y los dominios cookie-less y su influencia en el rendimiento de un sitio web. Si bien es cierto que no estoy nada de acuerdo con distribuir una web dinámica por un CDN, ya que eso destrozaría todo el sentido SEO que se le puede llegar a dar, sí que se pueden plantear soluciones para los contenidos estáticos.

Una de estas soluciones es el uso de los dominios sin cookies, que básicamente lo que son es sitios donde almacenar información que no permita recibir o enviar cookies. Y es que las imágenes, estilos o javascripts no necesitan, para nada, enviar o recibir cookies ya que no las van a saber interpretar, y quieras que no, si vas sumando bytes en las conexiones, al final del día son unos cuantos.

La primera pregunta que hay que hacerse es qué se debe elegir a la hora de plantear montar un dominio sin cookies. La idea es que sea un dominio para contenidos estáticos, es decir, imágenes, ficheros cacheables, etc; y, el siguiente planteamiento es obvio: ¿por qué no usar un subdominio? Muy sencillo, depende de cómo esté configurado un dominio o sus cookies, es posible que estas se pasen también a subdominios, por lo que, finalmente, llegamos a una conclusión: los estáticos han de estar en un dominio separado, pensado para estáticos sin cookies (y ya puestos, con compresión y otras cosas más).

Con esto tendríamos algo como que:

www.dominionormal.com/imagenes/logo.png

se convertiría en algo como:

www.dominiosincookies.com/imagenes/logo.png

La verdad es que encontrar información sobre cómo conseguir configurar un servidor web sin cookies no me ha sido nunca tarea sencilla. Supongo que otros servidores web distintos de Apache debe ser algo factible, sobretodo en los más modernos, pero en Apache las cookies vienen configuradas por defecto y no se pueden eliminar. Al menos no se pueden eliminar si no se usan algunas cosas extra.

Para poder usar esto, necesitamos el módulo de Apache mod_headers que habitualmente viene configurado y activado. Una vez eso, en el fichero de configuración de Apache, dentro del VirtualHost del dominio que hayamos decidido configurar como sin-cookies, tendremos que añadir estas dos líneas:

RequestHeader unset Cookie
Header unset Set-Cookie

Creo que son bastante claras en lo que hacen… pero, en resumen, se comen y no envían ninguna galleta.

Aun todo esto, algo que no he probado y que podría funcionar, es una configuración que podría ser parecida a esta que debería funcionar tanto en la configuración global, como en la del .htaccess, aunque como digo, no lo he podido testear.

<FilesMatch "\.(ico|gif|jpg|png|flv|pdf|mp3|js|css|xml)$">
Header set Cache-Control "max-age=2592000"
Header always unset Set-Cookie
Header unset ETag
FileETag None
</FilesMatch>

En este caso, sin necesidad de un dominio distinto, lo que el servidor hace es que cuando los ficheros que se solicitan coinciden con alguna de las extensiones que hay en la lista, ejecutaría esas líneas, que básicamente son una caché de 1 mes, eliminar cookies y activar los ETag.

Otra cosa a tener en cuenta es todo lo contrario… ¿y si necesitamos que las cookies siempre se queden un mínimo de tiempo y que no se eliminen cuando se cierra el navegador? Pues esto es tan sencillo como añadir una línea tal que así en el propio .htaccess:

CookieExpires 2592000

La cifra que acompaña son el número de segundos desde que se crea la primera vez la cookie, en este caso 2592000 corresponde a 30 días.

En fin, y creo que esto, una vez más, espero que de algunas pistas sobre cómo y qué revisar para montar algo de este estilo… sin duda algo interesante a tener en cuenta si le sumamos trabajar con CDN.

NOTA: Para los usuarios de WordPress, existe una variable global interesante que limita las cookies a sólo lo que se le diga:

define('COOKIE_DOMAIN', 'www.ejemplo.com');

NOTA: Para los que no quieran registrar un dominio, se puede usar una cosa que hay en pruebas en 2Static.it.

Los servidores de Google

Siempre ha habido un montón de información extraña sobre los servidores de Google y cómo se organiza. Desde que estoy metido en el mundo de los centros de datos en Digital Parks he podido ver algunos de los problemas a los que los webmaster normalmente no se enfrentan, como es la electricidad y la refrigeración de las máquinas, o el simple sistema de conectividad de redes…

En fin, os dejo con una serie de vídeos interesantes sobre el funcionamiento de los centros de datos y contenedores que usa Google.

AMPLIACIÓN: Pero antes, de ver los vídeos, me molaría comentarios sobre una patente que ha registrado gente de Google en la que se especifican cómo deberían ser una serie de servidores bastante más eficaces todavía de los que se presentan aquí, y que Google lleva trabajando desde hace 3 años. Descargar información e imágenes.

Además de estos vídeos hay varias presentaciones sobre lo que Google hace para que sean más eficientes y “verdes”.

La mejor forma de hacer una redirección

Los sitios web van y vienen… y como no queremos perder información ni generar un montón de errores en la red de redes, lo mejor es poder migrar información de un sitio a otro fácilmente.

Y como ya comenté una vez, el 302 no es una redirección, sino que lo son el 301 y 307, por lo que si queremos migrar todo lo relacionado a un sitio, deberemos aplicar una de estas, en este caso, la redirección 301.

Hay que partir de la base de que las redirecciones en HTML (a pelo) no funcionan, al menos no para lo que nosotros queremos. Esto significa que hacer una redirección con el meta-refresh o a través de JavaScript no sirven para migrar contenidos o pesos, sino que el usuario cambia de “página vista” a un nuevo lugar. Hay que tener en cuenta que esto se ha usado tiempo atrás como método de spam, así que hay muchas posibilidades de que los buscadores consideren tu sitio un poco indeseable.

En estos casos en los que no disponemos de un sistema para generar redirecciones 301 programadas lo mejor es utilizar el sistema de “rel=canonical” que propuso Google y el resto de buscadores y que, en algunos casos, se puede usar para redirigir tráfico entre dominios.

¿Se puede utilizar rel=”canonical” para sugerir una URL canónica de un dominio completamente distinto?

Existen situaciones en las que no resulta fácil configurar los redireccionamientos. Así ocurre, por ejemplo, cuando hay que realizar una migración a un nuevo nombre de dominio a través de un servidor web que no puede crear redireccionamientos de servidor. En esos casos se puede utilizar el elemento de enlace rel=”canonical” para especificar la URL exacta del dominio que se prefiere para la indexación. Aunque el elemento de enlace rel=”canonical” se considera una sugerencia y no una directiva incuestionable, intentamos seguirlo siempre que es posible.

Básicamente sería incluir en el “head” de cada una de las páginas a redireccionar algo como:

<link rel="canonical" href="http://www.nuevodominio.com/nueva-carpeta/nuevo-fichero.html">

En el caso en el que sí que tengamos acceso al menos al servidor Apache, aunque no tengamos un lenguaje de programación para hacerlo, es utilizar el .htaccess del servidor. Por norma general, debería de funciona, aunque si tu proveedor de Internet es un poco rata puede que no te sirva mucho…

En este caso tenemos un par de opciones… la más sencilla sería esta…

Redirección 301 usando .htaccess

Redirect 301 /vieja-carpeta/viejo-fichero.html http://www.nuevodominio.com/nueva-carpeta/nuevo-fichero.html

Otra opción es usar el mod_rewrite, también del .htaccess y que en este caso usaríamos, por ejemplo, para controlar si el sitio tiene o no las www.

Redirección 301 usando mod_rewrite

RewriteEngine On
RewriteCond %{http_host} ^nuevodominio.com
RewriteRule ^(.*) http://www.nuevodominio.com/$1 [R=301,L]

En el caso de querer hacerlo con Internet Information Server (IIS) podemos seguir estos pasos…

Redirección 301 usando Internet Information Server (IIS)

  • En el Internet Services Manager, hacer botón derecho en el fichero o carpeta a redireccionar.
  • Seleccionar la opción: redireccionar a una URL.
  • Añadir la dirección URL de destino
  • Seleccionar la opción URL exacta y redirección permanente.
  • Pulsar en Aplicar / OK

Si trabajamos con alguno de los siguientes lenguajes de programación, suele ser bastante sencillo, aunque se puede complicar según queramos hacerlo más o menos extensible. Por ejemplo:

Redirección 301 usando ASP

<%@ Language=VBScript %>
<%
Response.Status="301 Moved Permanently"
Response.AddHeader "Location", "http://www.nuevodominio.com/"
%>

Redirección 301 usando ASP.net

<script runat="server">
private void Page_Load(object sender, System.EventArgs e)
{
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location","http://www.nuevodominio.com/");
}
</script>

Redirección 301 usando ColdFusion

<cfheader statuscode="301" statustext="Moved permanently">
<cfheader name="Location" value="http://www.nuevodominio.com/">

Redirección 301 usando Java

<%
response.setStatus(301);
response.setHeader("Location", "http://www.nuevodominio.com/");
response.setHeader("Connection", "close");
%>

Redirección 301 usando Perl

$q = new CGI;
print $q->redirect(" http://www.nuevodominio.com/ ");

Redirección 301 usando PHP

<?php
header("HTTP/1.1 301 Moved Permanently");
header("Location: http://www.nuevodominio.com/");
exit;
?>

Redirección 301 usando Ruby o Ruby on Rails

head :moved_permanently, :location => "http://www.nuevodominio.com/

De todas formas, estas redirecciones mandarían cualquier página a la principal, y no a la correspondiente. para eso, por ejemplo en PHP, se podría hacer algo como esto:

<?php
header("HTTP/1.1 301 Moved Permanently");
$accion = "Location: http://www.nuevodominio.com".$_SERVER["REQUEST_URI"];
header($accion);
?>

En este caso se incluye la variable de servidor REQUEST_URI que lleva toda la dirección URL exceptuando el dominio…

Y con esto un minicursillo de redirecciones en muchos lenguajes de programación distintos… al menos espero que a alguien le sirva para tener un punto de inicio si necesita migrar un dominio y no quiere perder todo el trabajo que haya podido hacer históricamente en él.

Migrando de servidor

Seguramente algunos habréis notado que ayer tarde e incluso esta noche el sitio está haciendo un poco el tonto… y es que me ha dado por migrar el servidor web a algo mejor.

Con mejor no quiero referirme a que la máquina sea más potente (la verdad, no tengo ni idea de qué tiene) pero sí que me he dedicado a mejorar su configuración, y en vez de usar una máquina con Plesk he pedido una máquina “a pelo”.

Configurar un servidor web desde cero, con su PHP, con su SQL, todo de forma más o menos distribuida es un tema bastante interesante en cuanto a la escalabilidad que te da. Por ahora estoy en una primera fase, aunque pronto espero hacer crecer la máquina y lo que hay dentro.

En fin… poco más a explicar… voy a dejar unas cuantas horas hasta que se acaben de replicar las DNS y esas cosas… y así ya mañana sigo publicando más y mejor…

CSSTidy: optimizar CSS es fácil

Una de las cosas por las que no solemos preocuparnos mucho es por los CSS. Solemos hacer un CSS más o menos bien formado, pero a partir de ahí nos limitamos a subirlo al servidor y poco más. Pero… si os digo que se puede ahorrar hasta un 25% en un CSS, ¿no sería interesante aplicarlo?

La idea es que los CSS, al ser un elemento de los que se han de cargar antes que “se pinte” el HTML interesa que sea rápido en descargar. Además, es un elemento que suele estar en todas las páginas, y aunque el navegador suele cachearlo, vale la pena que ocupe poco…

Una de las herramientas que podemos implementar directamente en nuestro servidor es el CSSTidy, que tiene unas opciones para PHP bastante sencillas.

El ejemplo básico podría ser este:

<?php
include('class.csstidy.php');
$css_code = "a {
  color:black;
  background-color:blue;
}";
$css = new csstidy();
$css->set_cfg('remove_last_;',TRUE);
$css->parse($css_code);
echo $css->print->formatted();
?>

Un ejemplo no tan básico podría ser este:

<?php
$css_code = file_get_contents("http://www.example.com/estilo.css");
include("class.csstidy.php");
$css = new csstidy();
$css->set_cfg("preserve_css",true);
$css->load_template('high_compression');
$css->parse($css_code);
$min = $css->print->plain();
?>

Las opciones que yo dejaría son (aunque puede que se pueda mejorar con alguna otra combinación):

  • remove_bslash = true;
  • compress_colors = true;
  • compress_font-weight = true;
  • lowercase_s = true;
  • optimise_shorthands = 0;
  • remove_last_; = true;
  • case_properties = 0;
  • sort_properties = true;
  • sort_selectors = true;
  • merge_selectors = 1;
  • discard_invalid_properties = true;
  • css_level = ‘CSS2.1’;
  • preserve_css = true;
  • timestamp = false;

En fin, una interesante herramienta que podemos ver en acción directamente en CSS Compressor, que es la herramienta que habitualmente utilizo yo.

Cómo hacer muchas peticiones HTTP simultáneas en PHP

Uno de los problemas que habitualmente nos pueden frenar la carga de un sitio es si leemos mucha información de varios sitios de forma simultánea, como podría ser la lectura de varios feeds. Y es que habitualmente se usa la función file_get_contents() que tiene una cosa: es síncrona, es decir, hay que ejecutarla, esperar a que finalice, y volver a ejecutarla… pero ¿por qué esperar a qué acabe de leer para hacer otra llamada?

Para hacerlo podemos utilizar las funciones curl_multi_* que básicamente permiten hacer muchas llamadas cURL en muy poco tiempo. No es del todo asíncrono y en paralelo, pero casi casi se podría considerar como una opción.

El tiempo total de estas peticiones será la de la más lenta de todas, de forma que si tenemos algo como esto:

  • URL 1: 2,3 segundos
  • URL 2: 0,3 segundos
  • URL 3: 1,4 segundos
  • URL 4: 1,8 segundos
  • URL 5: 3,0 segundos

de la forma normal tardaríamos 8,8 segundos 8que es la suma de todos, aunque habría que sumarle los tiempos de conexión y desconexión), y de la forma que propongo se tardaría un poco más de 3,0 segundos, que es el tiempo de la más lenta de todas.

¿Cómo hacer esto?

<?php
function PeticionMultiple($urls, $opciones = array()) {
  $curly = array();
  $resultado = array();
  $pm = curl_multi_init();
  foreach ($urls as $id => $d) {
    $curly[$id] = curl_init();
    if(is_array($d) && !empty($d['url'])) {
      $url = $d['url']
    } else {
      $url = $d;
    }
    curl_setopt($curly[$id], CURLOPT_URL, $url);
    curl_setopt($curly[$id], CURLOPT_HEADER, 0);
    curl_setopt($curly[$id], CURLOPT_RETURNTRANSFER, 1);
    if (!empty($d['post'])) {
      curl_setopt($curly[$id], CURLOPT_POST, 1);
      curl_setopt($curly[$id], CURLOPT_POSTFIELDS, $d['post']);
    }
    if (!empty($opciones)) {
      curl_setopt_array($curly[$id], $opciones);
    }
    curl_multi_add_handle($pm, $curly[$id]);
  }
  $ejecutando = null;
  do {
    curl_multi_exec($pm, $ejecutando);
  } while($ejecutando > 0);
  foreach($curly as $id => $c) {
    $resultado[$id] = curl_multi_getcontent($c);
    curl_multi_remove_handle($pm, $c);
  }
  curl_multi_close($pm);
  return $resultado;
}
?>

Y el código de invocación:

<?php
$urls = array(
  'http://javiercasares.com/feed/atom/',
  'http://www.ethek.com/feed/atom/',
  'http://www.tumanitas.com/feed/'
);
$r = PeticionMultiple($urls);
print_r($r);
?>

Con esto, en principio, conseguiríamos múltiples peticiones para leer feeds de muchos sitios lo más rápido posible…

Relacionado: interesante la función EpicCurl del código EpiCode.

Open Standard Media (OSM) Player

Uno de los poyos que siempre me he encontrado a la hora de poner un reproductor de vídeo en la web es que en la mayoría de casos sólo aceptaba vídeos flash (.flv). La cosa es que con el HTML 5 y con jQuery se han montado un reproductor llamado Open Standard Media que tiene muy buena pinta, es código abierto y gratuito.

Entre otras cosas, permite el uso de HTML 5, soporta los nuevos elementos audio y video del HTML 5 con los formatos estándar, para el resto de formatos monta un reproductor Flash, se le puede cambiar el diseño de una forma sencilla gracias al uso de ThemeRoller, permite la integraciónd e vídeos de Vimeo y Youtube (simplemente indicando la URL), tiene la opción de listados de vídeos.

La instalación es muy sencilla, ya que sólo hay que añadir un par de JavaScript en la cabecera, poner el código básico del reproductor, y al final de la página, puedes incorporar un listado de los vídeos o una llamada a un XML que los tenga. Aun así, creo que la opción más sencilla es la de usar la librería en PHP, que con unas pocas líneas funcionaría:

<?php
include("OSMPlayer.php");
$player = new OSMPlayer(array(
  'file' => 'http://www.mysite.com/files/myvideo.flv',
  'image' => 'http://www.mysite.com/files/myimage.jpg',
  'disablePlaylist' => true
));
?>
<html>
  <head>
    <title>Open Standard Media (OSM) Player: PHP Demo</title>
    <script type="text/javascript" src="jquery-ui/js/jquery.js"></script>
<?php
print $player->getHeader();
?>
  </head>
  <body>
    <h2>Open Standard Media (OSM) Player</h2><br/>
<?php
print $player->getPlayer();
?>
  </body>
</html>

Con esto tendríamos un reproductor sencillo, aunque también se puede complicar un poco si le añadimos una lista de reproducción que nos permita visualizar decenas de vídeos… o si queremos modificar algunos parámetros, aquí está la lista de todos los disponibles…

face.com, la API que detecta caras en fotos

Aún no sé la utilidad de esta herramienta, pero como pronto, me ha parecido genial algo así… quizá podría servir para que al subir una foto a una red social el sistema pueda llegar a etiquetar a alguien de forma automática.

Y es que face.com ha abierto su API a desarrolladores, y la verdad es que para ser abierto y gratuito, tiene buena pinta y da cierta información…

Lo bueno de este sitio es que tienen varios ejemplos interesantes para varios lenguajes de programación e incluso integración con facebook y twitter.

La idea es que con una simple petición a través de la librería en PHP, como podría ser:

$face->faces_detect('http://farm3.static.flickr.com/2566/3896283279_0209be7a67.jpg');

o una petición vía REST:

http://api.face.com/faces/detect.xml?api_key=4b4b4c6d54c37&api_secret=&urls=http://farm3.static.flickr.com/2566/3896283279_0209be7a67.jpg

Se puede obtener un código tal que así:

<?xml version="1.0" encoding="utf-8"?>
 <response>
  <photos list='true'>
   <photo>
    <url>http://farm3.static.flickr.com/2566/3896283279_0209be7a67.jpg</url>
    <pid>F@53f98f0f3ffeef58413190e657e297ff_4b4b4c6d54c37</pid>
    <width>333</width>
    <height>500</height>
    <tags list='true'>
     <tag>
      <tid>TEMP_F@53f98f0f3ffeef58413190e657e297ff_4b4b4c6d54c37_51.05_60.40_0</tid>
      <threshold></threshold>
      <uids>
      </uids>
      <label></label>
      <confirmed></confirmed>
      <manual></manual>
      <tagger_id></tagger_id>
      <width>11.71</width>
      <height>7.8</height>
      <center>
       <x>51.05</x>
       <y>60.4</y>
      </center>
      <eye_left>
       <x>49.18</x>
       <y>59.7</y>
      </eye_left>
      <eye_right>
       <x>52.5</x>
       <y>59.46</y>
      </eye_right>
      <mouth_left>
       <x>50.16</x>
       <y>62.03</y>
      </mouth_left>
      <mouth_center>
       <x>51.61</x>
       <y>61.86</y>
      </mouth_center>
      <mouth_right>
       <x>52.65</x>
       <y>61.86</y>
      </mouth_right>
      <nose>
       <x>51.55</x>
       <y>60.53</y>
      </nose>
      <ear_left></ear_left>
      <ear_right></ear_right>
      <chin></chin>
      <yaw>26.44</yaw>
      <roll>-6.19</roll>
      <pitch>19.47</pitch>
      <attributes >
       <face>
        <value>true</value>
        <confidence>0.7862</confidence>
       </face>
       <gender>
        <value>male</value>
        <confidence>95</confidence>
       </gender>
       <glasses>
        <value>true</value>
        <confidence>96</confidence>
       </glasses>
       <smiling>
        <value>true</value>
        <confidence>93</confidence>
       </smiling>
      </attributes>
     </tag>
    </tags>
   </photo>
  </photos>
  <status>success</status>
  <usage>
   <used>1</used>
   <remaining>unlimited</remaining>
   <limit>unlimited</limit>
   <reset_time_text>unlimited</reset_time_text>
   <reset_time>0</reset_time>
  </usage>
 </response>

Este código te envía la información de la fotografía, y los puntos que reflejan cada una de las caras, indicando los ojos, boca, nariz, orejas, mentón, y la curvatura y/o posición de la cara, si es hombre o mujer, si lleva gafas o no y si está sonriendo.

Lector de discos duros SATA externo

Hace unos días que me puse a hacer un poco de limpieza de “elementos tecnológicos” en casa (oseasé, a tirar mierdas de placas, cables y demás porquería que se acumula) y hablando con Ana de Traxtore me comentó que tenían una cosilla que me podría ser útil para los discos externos.

Y es que cuando empiezas a tener 5 discos por ahí sueltos, y luego empiezas a desmontar cajas de esas con discos USB y demás, y te apalancas con 8 discos, se hace la cosa ingestionable. Así que finalmente acabé comprándome un disco duro de 2TB para poder quitarme el resto de en medio sumado a un adaptador Sharkoon SATA Quickport Pro. Hay varias versiones de este “gadget”, unas con acceso IDE, otras para dos discos…

Lo mejor de este adaptador es que tiene salida eSATA, y como mi portátil tiene entrada: ¡bingo! La transferencia de ficheros está sobre los 80 Mb/s que no es poco… es decir, que se mueven ficheros gigantes (como series) en muy poco tiempo. Que conste que también tiene salida USB, aunque no la he probado, pero supongo que será algo más lento…

En fin, que ahora que me he quitado todos los marrones de en medio y que tengo un disco de 2 Teras para llenar, soy una persona más feliz.

font dragr: prueba tus fuentes sólo arrastrándolas

Aunque hago un parón en cuanto a publicar de HTML 5, he encontrado esta herramienta, web o como queramos llamarlo que tiene su gracia, sobretodo si te dedicas a maquetar o eres diseñador.

El sitio se llama font dragr y básicamente es una página en la que arrastrando un fichero de tipo de letra -truetype (ttf), opentype (otf), scalable vector graphics (svg) o Web Open Font Format (WOFF)- te actualizará los textos de la página con esa fuente. Además, el contenido original de la página es editable, por lo que puedes poner un texto de ejemplo y así ver cómo quedaría directamente en el navegador web.

Otro detalle que ya avisan es que lo de arrastrar sólo funciona con in Firefox 3.6, que es, en estos momentos, el único que soporta esas funcionalidades de HTML5. Entre las cosas que se usan encontramos el uso del sitio en modo offline, la API de arrastrar y soltar, la de gestión de ficheros, la de editar contenidos, la del uso del @font-face, gradientes, bordes redondeados, sombras…

HTML 5: formularios, el elemento 2.0 (parte 2)

Hace unos días explicaba principalmente el elemento input de los formularios en HTML 5 y hoy toca el resto de elementos. Y es que no es moco de pavo todo lo que hay.

fieldset

Este elemento se utiliza para agrupar varios elementos de un formulario… si un formulario tiene varios “bloques” o contenidos distintos, se deberían agrupar con este elemento. Si se le indica el atributo disabled entonces los elementos contenidos en él también lo están. También se le puede indicar el atributo form que hace referencia al formulario padre, y el atributo name, para acceder externamente con un nombre único.

legend

Si se le quiere poner una cabecera descriptiva a cada uno de los fieldset, el legend es el elemento que debemos utilizar. Con este texto podremos poner una cabecera a cada uno de los bloques que pueden formar un formulario compuesto de muchos contenidos.

label

Los label son los textos que acompañan a un elemento, como puede ser un input y que vienen a decir qué es de lo que se habla (que no una ayuda sobe el campo en sí). Este elemento puede tener el atributo for que se usaría para hacer referencia al name del elemento.

<label><input type="checkbox" name="lost"> Lost</label<

button

Es simplemente un botón, y es que los button son los elementos que permiten acabar de interactuar con el formulario. Botones hay de 3 tipos que se indican con el atributo type:

  • submit: que permite que la información del formulario se envíe.
  • reset: que deja el formulario en su estado inicial.
  • button: que simplemente, no hace nada.

El atributo form indica el nombre del formulario al que hace referencia, y el name es el nombre que le asignamos al propio botón. Además de estos, también está disponible el atributo disabled que básicamente hace que el botón no pueda ejecutarse, y de esta forma queda limitado a una serie de verificaciones previas. Con el atributo autofocus el sistema automáticamente mandará el foco al botón.

select

Los select hacen referencia a un listado de opciones. Esta selección por defecto es simple (sólo un elemento) aunque si se le añade el atributo booleano multiple podremos seleccionar más de una opción. De la selección se elegirá uno o más option que son los elementos que se usan para indicar cada una de las opciones.

El atributo size permite definir la cantidad de opciones visibles por defecto, que puede ser una o más. En el caso de que haya el atributo multiple activo, por defecto habrá 4.

<select name="allowedunits" multiple size="3">
  <option value="1" selected>Miner</option>
  <option value="2" selected>Puffer</option>
  <option value="3">Snipey</option>
  <option value="4">Max</option>
  <option value="5" selected>Firebot</option>
</select>

optgroup

Como ya el nombre deja ver, optgroup representa una agrupación de elementos option que tienen una agrupación común. Aunque en sí no tiene mucho significado, básicamente es eso, una agrupación de elementos de una misma temática, tipo o como se le quiera llamar, pero poco más.

Puede llevar varios atributos, como el disabled si queremos desactivar todas las opciones contenidas, o el label, que será el nombre que tenga ese grupo de cara a los usuarios.

option

El elemento option puede formar parte de un select, de un optgroup o de un datalist y básicamente es una de las opciones a elegir de entre unas cuantas. Al igual que muchos otros elementos, puede estar disabled y por tanto no ser utilizada en la selección.

Por norma general suele ir acompañado de un valor value que será lo que realmente se mande como información a la hora de enviar o trabajar con el formulario. Como información añadida podemos usar el label, que podría tratarse como el título o mini-descripción de esa opción. Por supuesto, otro de los atributos importantes es el selected que se usa en aquellos momentos en que queremos tener elementos seleccionados por defecto.

datalist

El elemento datalist está pensado para ser un sistema de “sugerencias”. Básicamente es un contenedor en el que se incluyen distintas opciones que corresponderían a un campo. De esta forma, si alguien empeiza a escribir en un input y coindice de alguna manera con alguno de los contenidos, le aparecerá como una sugerencia.

<input type="text" name="favcharacter" list="characters">
  <datalist id="characters">
    <option value="Homer Simpson">
    <option value="Bart">
    <option value="Fred Flinstone">
  </datalist>

textarea

Un textarea es un elemento de entrada de datos libre y multilínea, es decir, un sitio donde escribir “lo que te da la gana”. Este elemento tiene bastantes atributos que le permiten hacer algunas cosas.

Para empezar podemos aplicarle el elemento readonly que básicamente impide que los usuarios puedan cambiar el contenido del elemento. Para indicar el tamaño del elemento podremos usar los atributos cols y rows que indican la cantidad de caracteres por columna y filas que se permiten; por defecto hay 20 caracteres por columna y 2 filas. Además, para que no se corten, o sí, las palabras que se puedan incluir en este elemento, podemos usar el wrap indicando el valor soft (por defecto) o hard con el qe controlamos saltos de línea y similares. En el caso de indicar un valor de maxlength le estamos diciendo al textarea que hay un límite máximo de caracteres, y que a partir del límite no ha de dejar continuar. SI queremos que el usuario tenga que rellenar obligatoriamente el campo, hay que indicarle el valor booleano required; también es posible deshabilitarlo con un disabled.

Un detalle interesante, es que, al igual que el input, este elemento también permite el atributo placeholder, que es una pequeña ayuda (de unas pocas palabras) que explique qué ha de contener el elemento.

keygen

Sin duda uno de los elementos más enigmáticos del HTML 5 es el elemento keygen. Este elemento básicamente lo que hace es generar una clave privada y una pública para que el navegador y el servidor puedan comunicarse. La clave la genera automáticamente el navegador, y supongo que mediante AJAX o algún otro sistema se podrá mantener una “conversación” entre el navegador y servidor… A ver si encuentro algún ejemplo de uso, porque no he visto nada todavía.

Permite algunos atributos como challenge o el keytype que es el tipo de codificación de la clave, que por ahora sólo acepta un valor que es rsa.

output

Otro de los nuevos elementos es el output… y es que si hay input, ¿por que no output? Y básicamente es eso… los inputs son los “cajetines” para escribir cosas, y en alguna ocasión, los propios inputs han servidor como “cajetín” de cálculo o similar… pues esto es un “cajetín” que se usa como resultado de algo.

<form onsubmit="return false">
  <input name=a type=number step=any> +
  <input name=b type=number step=any> =
  <output onforminput="value = a.valueAsNumber + b.valueAsNumber"></output>
</form>

progress

Este es uno de los elementos que todavía no queda claro si estará en la versión final de HTML 5 o se quedará por el camino, como ya ha ocurrido con algún otro en versiones anteriores. Y es que progress viene a indicar el proceso que lleva una tarea, como si de una barra de progreso se tratase… y está pendiente de aprobación porque no existe nada que haga referencia la tarea en sí.

El elemento tiene dos posibles parámetros que son value y max que indicarían el valor a mostrar y el valor máximo que puede tener, partiendo de la base de que va entre 0 y max.

meter

Y el elemento que mataría el progress es el meter, que hace lo mismo pero diferente… básicamente es un sistema que permite crear “escalas” de cosas, independientemente de lo que sea. Un ejemplo podría ser el tamaño ocupado o libre de un disco duro.

Este elemento tiene bastantes atributos, y casi diría que hay que ponerlos todos:

  • value: Es el valor actual de todos los posibles.
  • min: El valor mínimo posible para el rango. Por defecto es 0.0
  • max: El valor máximo posible para el rango. Por defecto es 1.0
  • low: Es el valor por el que, por debajo, sería “un problema”. Por defecto es el mismo que min.
  • high: Es el valor por el que, por encima, sería “un problema”. Por defecto es el mismo que max.
  • optimum: Es el valor óptimo para la medidicón. Por defecto es la media entre min y max, o 0.5

Algunos ejemplos:

Storage space usage: <meter value=6 max=8>6 blocks used (out of 8 total)</meter>

Voter turnout: <meter value=0.75><img alt="75%" src="graph75.png"></meter>

Tickets sold: <meter min="0" max="100" value="75"></meter>

<dl>
<dt>Radius: <dd><meter min=0 max=20 value=12>12cm</meter>
<dt>Height: <dd><meter min=0 max=10 value=2>2cm</meter>
</dl>

Y hasta aquí lo que corresponde a los formularios del HTML 5. Algunas novedades interesantes sobretodo pensadas para lo que en el futuro puede traernos “la web 2.0” e incluso, si hace falta la 2.5 o la 3, quién sabe…

HTML 5: formularios, el elemento 2.0 (parte 1)

Si hay un elemento que ha evolucionado enormemente con respecto a las versiones anteriores sin duda es el de los formularios. Y es que en estos últimos 10 años de ha construido lo que se llama web 2.0 (también conocido como yo ya había visto eBay en el 98 y era una web 1.0, así que alguien me está tomando el pelo). En fin, no quiero discutir sobre cómo se llaman las cosas en internet, porque la gente las llama como le da la gana.

Aunque sí que es cierto que hay algo que no se puede obviar, y es que el hecho de que los sitios web sean más interactivos con el usuarios ha hecho que las necesidades de diferencias y complementar algunos elementos y atributos crezcan.

Y esa es una de las razones por las que la parte de formularios hay que dividirla en 2 partes, porque hay algunos elementos que son “casi interminables”… Comenzamos.

form

El elemento form es el que agrupa cada uno de los formularios. Actúa como contenedor y a la vez permite que se ejecuten las entradas de datos. Tiene algunos atributos que permiten que el usuario interactúe de formas distintas:

  • accept-charset: Indica el MIME-type con el que se mandará la información del formulario.
  • action: Indica la dirección URL a la que se enviará la información del formulario.
  • autocomplete: Permite sólo on y off (por defecto on) y permite que el navegador rellene aquellos campos input de forma automática
  • enctype: Indica la codificación de la información que se enviará. Tiene 3 posibles estados: application/x-www-form-urlencoded (por defecto si no se indica), multipart/form-data y text/plain.
  • method: Indica el tipo de cabecera por el que se enviará la información. Permite 4 estados: GET (por defecto), POST, PUT y DELETE.
  • name: Es el nombre del formulario. Si se indica no puede estar vacío, pero puede no ponerse (y el sistema lo creará automáticamente de forma interna).
  • novalidate: Un atributo booleano que indica si la información se validará o no.
  • target: Al igual que los enlaces, permite los valores: _blank, _self (por defecto), _parent o _top.

input

El principal elemento para la introducción de datos sigue siendo el input, y en esta ocasión llega con muchísimos atributos y con muchos tipos de entradas.

El principal atributo del sistema de entrada de datos es el type. Hasta ahora los valores más habituales eran hidden o text que básicamente eran textos alfanuméricos, pero ahora eso ha cambiado, y la lista de tipos es bastante grande:

  • hidden: Es un contenido oculto que suele corresponder a una serie de caracteres alfanuméricos. Este valor no puede ser manipulado por el usuario.
  • text: Campo de texto que no permite saltos de línea. Si se añade el atributo value será el contenido original del campo.
  • search: Campo de texto que no permite saltos de línea y que indica que será usado como contenido de una búsqueda. Si se añade el atributo value será el contenido original del campo.
  • tel: Campo de texto que indica un número de teléfono. No tiene ningún formato en particular, por lo que se pueden incluir espacios o guiones, por ejemplo.
  • url: Campo de texto en el que se pueden incluir direcciones URL absolutas. Se puede hacer una especie de autosuggest junto a otros elementos (ver ejemplo más abajo).
  • email: Campo de texto que permite incluir correo electrónico. Principalmente si se incluye el atributo value permite una única dirección, y si es el multiple puede incluir varias.
  • password: Campo de texto, pero que aparece oculto para el usuario y así proteger la información.
  • datetime: Permite una fecha y hora válida en un formato como 0037-12-13T00:00Z, 1979-10-14T12:00:00.001-04:00 o 8592-01-01T02:09+02:09. Si se indica un número, serán los milisegundos transcurridos desde el 01/01/1970.
  • date: Permite indicar una fecha. El atributo value indica la fecha en cuestión y el min y max los límites del rango de fecha. El atributo step marca los milisegundos (por defecto 1 día) entre un valor y otro. El formato sería 2010-03-09.
  • month: Permite indicar un mes. Tiene los mismos atributos que date. El formato sería 2010-03.
  • week: Permite indicar la semana del año. Tiene los mismos atributos que date. El formato sería 2010-W03 (tercera semana).
  • time: Permite indicar una hora. Tiene los mismos atributos que date. El formato sería 17:23 y opcionalmente los segundos, 17:23:59.
  • datetime-local: Es igual que el datetime pero no permite indicar ningún tipo de desfase horario.
  • number: Representa un valor numérico. Este puede ser negativo (si empieza con un guión -), seguido de un número y con decimales (si se le indica un punto .) y para acabar, un exponente, empezando con una letra e, seguida de un símbolo positivo + o negativo – y la cantidad exponencial. Un ejemplo completo sería: -12345.678e+90.
  • range: El tipo range permite un slide en donde el número (valor) no es lo 100% importante, sino que es el formato en el que se muestra. Un poco más abajo, un ejemplo.
  • color: Este elemento representa un color. El formato es del estilo #A0F59E (o sea, el RGB).
  • checkbox: Este elemento ya existía, y es el que permite elegir unas cuantas opciones de un grupo de ellas.
  • radio: Al igual que el anterior, permite elegir de un grupo, pero sólo una de las opciones.
  • file: Éste permite la opción de subir archivos. Con él se pueden seleccionar archivos que luego serán enviados al sistema. Si se le indica el atributo multiple se podrán seleccionar varios ficheros a la vez. Además, si se indica el atributo accept se puede limitar ficheros según su MIME-type, por ejemplo con audio/*, video/* o image/*.
  • submit: Este es el botón que permite el envío de información al sistema. Puede ir acompañado de otros atributos que hacen que “ese botón” haga cosas que no son las generales del formulario. Los parámetros son: formaction (que hace ese botón), formenctype (la codificación), formmethod (la forma de envío), formnovalidate (la validación) y formtarget (en qué “ventana” se abre).
  • image: Es igual que el submit pero permite ser una imagen en vez de un botón en sí. Para ello ha de ir acompañado del atributo src que indica la dirección URL de la imagen.
  • reset: Otro botón que si pulsas deja los valores del formulario en su valor inicial.
  • button: Un botón que “no hace nada”. Suele utilizarse habitualmente con funcione JavaScript que son los que realizan la acción.

Ejemplo de autosuggest gracias al type=”url”

<input type="url" name="location" list="urls">
<datalist id="urls">
  <option label="MIME: Format of Internet Message Bodies" value="http://www.ietf.org/rfc/rfc2045">
  <option label="HTML 4.01 Specification" value="http://www.w3.org/TR/html4/">
  <option label="Form Controls" value="http://www.w3.org/TR/xforms/slice8.html#ui-commonelems-hint">
  <option label="Scalable Vector Graphics (SVG) 1.1 Specification" value="http://www.w3.org/TR/SVG/">
  <option label="Feature Sets - SVG 1.1 - 20030114" value="http://www.w3.org/TR/SVG/feature.html">
  <option label="The Single UNIX Specification, Version 3" value="http://www.unix-systems.org/version3/">
</datalist>

que quedaría algo como:

Ejemplo de range

<input type="range" min="-100" max="100" value="0" step="10" name="power" list="powers">
<datalist id="powers">
  <option value="0">
  <option value="-30">
  <option value="30">
  <option value="+60">
</datalist>

que quedaría algo como:

Además de los distintos tipos que puede haber, existen distintos atributos, como ya he ido comentando en algunos de los tipos…

  • autocomplete: Permite el estado on y off y hace que el campo se rellene o no automáticamente si el sistema es capaz de identificar la información.
  • list: Es el valor de una lista identificada por el datalist. Con esto se pueden crear sistema de “autosugerencias”.
  • readonly: Es un valor booleano, y permite o no que el usuario pueda modificar el contenido.
  • size: Es el número de caracteres que se muestran por pantalla.
  • required: Otro atributo booleano que indica que ese elemento ha de incluirse obligatoriamente.
  • multiple: Permite que algunos elementos seleccionen más de un valor. Un ejemplo más adelante.
  • maxlength: Es la cantidad máxima de caracteres que se permite.
  • pattern: Indica el formato de contenido que tiene que tener… Por ejemplo: [0-9][A-Z]{3} sería un número seguido de 3 letras en mayúsculas.
  • min y max: Indican los valores mínimo y máximo que se permiten.
  • step: Indica el valor de paso cuando hay elementos que permiten saltar.
  • placeholder: Es un “ejemplo” del valor que se puede incluir.

Ejemplo de selección múltiple de cuentas de correo:

<label>Cc: <input type=email multiple name=cc list=contacts></label>
<datalist id="contacts">
  <option value="hedral@damowmow.com">
  <option value="pillar@example.com">
  <option value="astrophy@cute.example">
  <option value="astronomy@science.example.org">
</datalist>

Que se mostrará de una forma similar a esto:

Ahora sólo queda el resto de elementos que puede incorporar en un formulario, pero eso será otro día.

Formas de validar un correo electrónico

Una de las cosas que habitualmente es necesario hacer es validar cuentas de correo. Hay muchas cosas a hacer, pero una de las primeras es saber si la cuenta de correo está “bien formada”. Un artículo muy interesante sobre distintas formas de verificar cuentas, basadas en una serie de expresiones que sí han de validar, y otras que no han de hacerlo.

La expresión que en principio se ajusta más a todas las posibilidades es la siguiente:

/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i

Con esto tendríamos un código similar a este:

<?php
$texto = "prueba@ejemplo.com";
$expresion = "/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i";
preg_match($expresion , $texto , $encuentros);
print_r($encuentros);
?>

De todas formas, quizá habría que hacer otra cosa, que es verificar si realmente la cuenta existe… y es que una cosa es que la cuenta de correo esté bien formada y otra es que pueda hacer una prueba de envío de correo sin necesidad de enviar. Para eso podríamos usar una clase como esta que se conecta a la máquina remota para verificar la existencia de la cuenta…

<?php
class SMTP_validateEmail {
  var $sock;
  var $user;
  var $domain;
  var $domains;
  var $port = 25;
  var $max_conn_time = 30;
  var $max_read_time = 5;
  var $from_user = 'pruebas@ejemplo.com';
  var $from_domain = 'localhost';
  var $nameservers = array(
    '192.168.0.1'
  );
  var $debug = false;
  function SMTP_validateEmail($emails = false, $sender = false) {
    if ($emails) {
      $this->setEmails($emails);
    }
    if ($sender) {
      $this->setSenderEmail($sender);
    }
  }
  function _parseEmail($email) {
    $parts = explode('@', $email);
    $domain = array_pop($parts);
    $user= implode('@', $parts);
    return array($user, $domain);
  }
  function setEmails($emails) {
    foreach($emails as $email) {
      list($user, $domain) = $this->_parseEmail($email);
      if (!isset($this->domains[$domain])) {
        $this->domains[$domain] = array();
      }
      $this->domains[$domain][] = $user;
    }
  }
  function setSenderEmail($email) {
    $parts = $this->_parseEmail($email);
    $this->from_user = $parts[0];
    $this->from_domain = $parts[1];
  }
  function validate($emails = false, $sender = false) {
    $results = array();
    if ($emails) {
      $this->setEmails($emails);
    }
    if ($sender) {
      $this->setSenderEmail($sender);
    }
    foreach($this->domains as $domain=>$users) {
      $mxs = array();
      list($hosts, $mxweights) = $this->queryMX($domain);
      for($n=0; $n < count($hosts); $n++) {
        $mxs[$hosts[$n]] = $mxweights[$n];
      }
      asort($mxs);
      array_push($mxs, $this->domain);
      $this->debug(print_r($mxs, 1));
      $timeout = $this->max_conn_time/count($hosts);
      while(list($host) = each($mxs)) {
        $this->debug("try $host:$this->port\n");
        if ($this->sock = fsockopen($host, $this->port, $errno, $errstr, (float) $timeout)) {
          stream_set_timeout($this->sock, $this->max_read_time);
          break;
        }
      }
      if ($this->sock) {
        $reply = fread($this->sock, 2082);
        $this->debug("< <<\n$reply");
        preg_match('/^([0-9]{3}) /ims', $reply, $matches);
        $code = isset($matches[1]) ? $matches[1] : '';
        if($code != '220') {
          foreach($users as $user) {
            $results[$user.'@'.$domain] = false;
          }
          continue;
        }
        $this->send("HELO ".$this->from_domain);
        $this->send("MAIL FROM: < ".$this->from_user.'@'.$this->from_domain.">");
        foreach($users as $user) {
          $reply = $this->send("RCPT TO: < ".$user.'@'.$domain.">");
          preg_match('/^([0-9]{3}) /ims', $reply, $matches);
          $code = isset($matches[1]) ? $matches[1] : '';
          if ($code == '250') {
            $results[$user.'@'.$domain] = true;
          } elseif ($code == '451' || $code == '452') {
            $results[$user.'@'.$domain] = true;
          } else {
            $results[$user.'@'.$domain] = false;
          }
        }
        $this->send("quit");
        fclose($this->sock);
      }
    }
    return $results;
  }
  function send($msg) {
    fwrite($this->sock, $msg."\r\n");
    $reply = fread($this->sock, 2082);
    $this->debug(">>>\n$msg\n");
    $this->debug("< <<\n$reply");
    return $reply;
  }
  function queryMX($domain) {
    $hosts = array();
    $mxweights = array();
    if (function_exists('getmxrr')) {
      getmxrr($domain, $hosts, $mxweights);
    } else {
      require_once 'Net/DNS.php'; // http://pear.php.net/package/Net_DNS/
      $resolver = new Net_DNS_Resolver();
      $resolver->debug = $this->debug;
      $resolver->nameservers = $this->nameservers;  
      $resp = $resolver->query($domain, 'MX');
      if ($resp) {
        foreach($resp->answer as $answer) {
          $hosts[] = $answer->exchange;
          $mxweights[] = $answer->preference;
        }
      }
    }
    return array($hosts, $mxweights);
  }
  function microtime_float() {
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
  }
  function debug($str) {
    if ($this->debug) {
      echo htmlentities($str);
    }
  }
}
?>
Categorías PHP

CSS Reset para HTML 5

Llevo varios días intentando encontrar un CSS Reset pero para HTML 5. Hasta ahora usaba el de Yahoo! YUI, pero incluso la versión 3 no da soporte a las nuevas etiquetas. Por eso me he decidido a revisar un poco los elementos que hay hasta ahora e intentar ofrecer una primera versión del CSS Reset para HTML 5.

html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td,hr,iframe,embed,object,legend{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup,a,small,q,time,samp,kbd,sup,sub,mark,col,colgroup,tbody,thead,tfoot,tr,td,th,label,input,button,textarea,select,optgroup,option,label,output{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;font-variant:normal;}i{font-style:italic;}b{font-weight:bold;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select{*font-size:100%;}header,article,section,aside,footer,nav,hgroup,address,figure,figcaption,video,audio,legend,datalist,optgroup,details,summary,command,menu{display:block;margin:0;padding:0;}

Se puede descargar el CSS Reset para HTML 5.

NOTA: Que conste que es una propuesta personal, basándome en la mayoría de los elementos que propone el HTML 5.

ACTUALIZACIÓN: Me han pasado este de Rich Clark.