Archivo de 'desarrollo'

10 cosas que quizás no sabías de PHP en la shell

Todos sabemos lo fantástico que es PHP como lenguaje de programación de páginas web. Lo que no muchos saben es que es igualmente fantástico como lenguaje para realizar programas multiplataforma para la shell. No todo es bash, Python, perl o Microsoft Powershell. Vamos a ver algunas de las cosas que quizás no sabías que podemos hacer con PHP en la consola.

Nota: Toda esta información está disponible en el manual de documentación de PHP.

1. Ejecutar un script

Podemos crearnos nuestros propios programas simplemente creando un fichero .php y ejecutándolo con el binario CLI php. Por ejemplo:

<?php
   echo "hola mundo!\n";
?>

Y ahora para ejecutarlo sólo tendríamos que llamar al binario php (php.exe en Windows) seguido del nombre del script. Supongamos que el ejemplo de arriba lo guardamos en un fichero que se llama test.php. Sería así:

$ php test.php

Y en sistemas Windows:

> php.exe test.php

De esta forma, los dos mostrarían en pantalla el texto “hola mundo!”. Fácil, verdad? :)

2. Hacer que el script se sea ejecutable

En Windows tendremos que asociar la extensión .php al binario php.exe. De esa forma, al hacer doble clic sobre un fichero, este se ejecutará. Sin embargo, si quisiéramos que se pudiera ejecutar desde la consola, tendríamos que crear un fichero por lotes .bat que realice la llamada.

@echo OFF
"C:\php\php.exe" test.php

En sistemas Unix es más sencillo. Tenemos que añadir en la primera línea la ruta al binario que ejecuta el script, y posteriormente otorgamos permisos de ejecución al fichero. Y ya está, ya es ejecutable desde la consola y desde el entorno gráfico. El fichero quedaría así:

#!/usr/bin/php
<?php
   echo "hola mundo!\n";
?>

Y después damos permisos de ejecución, y ya podremos ejecutarlo:

$ chmod +x test.php
$ ./test.php

3. Pasar parámetros al programa

Supongamos un comando cualquier de la consola, por ejemplo “ls”. Este comando acepta numerosos parámetros para controlar su comportamiento. En PHP también podemos pasar parámetros. Sólo tenemos que tener en cuenta una cosa: Si el script no tiene permisos de ejecución, hay que pasar los parámetros tras los caracteres “–”. Es así para prevenir que el binario php los considere parámetros suyos. Veamos un ejemplo de cómo hacerlo:

$ php test.php — parametro1 parametro2
$ ./test.php parametro1 parametro2

En el primer ejemplo ejecutamos el binario php y le pasamos como parámetro el nombre del script, por lo que en este caso añadimos los caracteres “–”. En cambio, en el segundo no es necesario, ya que el script tiene permisos de ejecución.

En sistemas Windows, como vimos arriba, habría que crear un fichero por lotes .bat, que para que pasara los parámetros a nuestro script quedaría así:

@echo OFF
"C:\php\php.exe" test.php %*

Bien, ya sabemos cómo enviar parámetros al script. Sólo nos queda saber cómo manejarlos. Pues muy sencillo. Hay dos variables globales para manejarlos:

  1. $argc – Contiene el número de parámetros que recibe el script
  2. $argv – Es un array con los parámetros en sí

4. Usar PHP en como consola interactiva

Al igual que ocurre con Python, PHP también puede ejecutarse en modo interactivo. Esto es, introducimos a mano los comandos en una “consola PHP”, y ésta los ejecuta al vuelo. Sólo hay que poner el parámetro “-a”. Por ejemplo:

$ php -a
Interactive shell

php > echo 5+8;
13
php > function sumarDos($n)
php > {
php { return $n + 2;
php { }
php > var_dump(sumarDos(2));
int(4)
php >

Además, pulsando el botón tabulador, tendrás autocompletado de variables, funciones, clases, constantes, método estáticos y constantes de clases.

5. Ejecutar una línea de código PHP al momento

Podemos ejecutar el código que queramos usando el modificador “-r”. Imaginemos que queremos aplicar un hash md5 a un fichero de texto, y no tenemos en nuestro sistema el comando md5sum. Podríamos hacer lo siguiente:

php -r 'echo md5(file_get_contents("fichero.txt"));'

Lo que hacemos es ejecutar los comandos que hay entre las comillas simples:

  1. Cargamos el contenido de ‘fichero.txt’.
  2. Le aplicamos el hash md5.
  3. Lo mostramos en pantalla.

¡Qué de cosas se pueden hacer con esto!

6. Pasar contenidos a PHP por tuberías

Muchas veces vemos comandos como:

echo "hola mundo" | wc -c

En este sencillo ejemplo lo que hemos hecho es en la salida estándar el texto “hola mundo”, que a su vez se envía a la entrada estándar del comando “wc -c”, que se encarga de contar el número de caracteres. ¿Cómo hacemos esto en PHP? Consultando la entrada estándar. En la versión CLI de PHP ponen a nuestra disposición 3 útiles constantes:

  1. STDIN – Stream a la entrada estándar.
  2. STDOUT – Stream a la salida estándar.
  3. STDERR – Stream a la salida de errores.

Si quisiéramos contar el número de caracteres del texto recibido por la entrada estándar en PHP, podríamos hacer lo siguiente:

echo "hola mundo" | php -r 'echo strlen(fgets(STDIN));'

7. Mostrar un fichero PHP sin comentarios ni espacios

¿Nunca te has preguntado cuántos caracteres útiles tiene un gran proyecto? Con el modificador “-w” puedes verlo, pues eliminará todos los caracteres en blanco innecesarios (incluidos tabulares, retornos de carro, cambios de línea, etc), y todos los comentarios. Además, también podrás así minimizar el tamaño de los ficheros. Por ejemplo:

$ php -w fichero.php

8. Consultar documentación desde la consola

Supón que deseas conocer los parámetros de una función, o los métodos de una clase… Puedes consultarlo desde la consola, siempre y cuando tu versión de PHP esté compilada con la extensión Reflection. Esta familia de modificadores nos da 4 opciones:

  1. “–rf” – Muestra información de una función o de un método de una clase.
  2. “–rc” – Muestra información de una clase (sus métodos, atributos, constantes…).
  3. “–re” – Muestra información de una extensión (directivas .ini, funciones, constantes, clases, etc).
  4. “–ri” – Muestra información sobre la configuración de una extensión. Esto es, lo mismo que devuelve phpinfo() para cada extensión. Si queremos ver la información general, usaremos “main” como nombre de extensión.

Veamos un ejemplo en el que consultamos información de la función md5():

$ php --rf md5
Function [ <internal:standard> function md5 ] {
  - Parameters [2] {
    Parameter #0 [ <required> $str ]
    Parameter #1 [ <optional> $raw_output ]
  }
}

Una opción estupenda cuando no tenemos acceso a internet, ni nos hemos descargado la documentación de PHP a nuestro equipo.

9. Decorar la sintaxis de nuestro código

PHP nos ofrece una forma sencillísima de generar una versión HTML de nuestro código, coloreando y destacando cada parte. Sólo tendremos que ejecutarlo con el modificador “-w”. Una vez hecho esto, podremos exportarla al formato que queramos (PDF, Word, XHTML, etc). Muy útil cuando queremos imprimir un código fuente.

$ php -w fichero.php

Además, al estar disponible desde el propio binario de php, es muy fácil generar una versión HTML coloreada de cada fichero.

10. La joya de la corona: definir código inicial, final, y para cada línea

Si no tenías suficiente con sed o awk, ahora con PHP vas a poder hacer maravillas. Tenemos estos cuatro modificadores:

  1. “-B” – Código PHP a ejecutar antes que todo lo demás.
  2. “-F” – Código PHP a ejecutar cuando finalice todo lo demás.
  3. “-R” – Código PHP a ejecutar por cada línea. Tendremos $argn con la línea que se está procesando, y $argi con el número de línea.
  4. “-F” – Fichero con el código PHP a ejecutar por cada línea.

Veamos un ejemplo muy didáctico y muy poco útil de la documentación de PHP. Supongamos que queremos contar el número de líneas de un proyecto. Accedemos a su directorio y ejecutamos lo siguiente:

$ find . | php -B '$lineas=0;' -R '$lineas += count(@file($argn));' -E 'echo "Total de líneas: $lineas\n";'

Veámoslo paso a paso:

  1. En primer lugar ejecutamos el comando “find” de la consola de Linux. Nos mostrará (recursivamente) todos los ficheros del directorio actual.
  2. El resultado se lo pasamos por la entrada estándar al binario php, el cual realiza lo siguiente:
    1. En el modificador “-B” especificamos el código inicial. Simplemente inicializamos la variable $lineas a cero.
    2. En el modificador “-R” decimos lo que se hará por cada línea recibida por la entrada estándar. En este caso, cada línea se corresponde con la ruta a un fichero:
      1. Como vimos arriba, $argn contiene el texto que se está procesando. Contiene la ruta a un fichero
      2. Pasamos esa ruta a la función file(), que genera un array donde cada índice contiene una fila del fichero leído.
      3. Consultamos el tamaño del array, es decir, el número de líneas del fichero.
      4. Incrementamos la variable $lineas con el valor obtenido en el paso anterior.
    3. Finalmente, el modificador ‘-E’ se ejecuta al finalizar todo lo demás, y nos muestra como resultado el número total de líneas de texto que hay en todos los ficheros de la carpeta actual.

Sencillísimo y muy potente. Lo que más complicado puede resultar es recordar los 3 modificadores (B,F,R) y las dos variables ($argn y $argi).

Traducción de la documentación de PHP a Español

Hace (mucho) tiempo que vengo pensando en colaborar en algún proyecto de software libre. Al fin y al cabo, hace años que casi todo el software que utilizo es libre. Gran parte del resultado es gracias al trabajo desinteresado de mucha gente por todo el mundo.

Pensando en qué puedo y qué me apetece aportar, llegué a la conclusión de que el proyecto adecuado era PHP. No en vano es el proyecto de software libre que me da de comer :) Si Apache no existiera podría utilizar lighttpd. Si Linux no existiera me instalaría algún BSD. Si MySQL tampoco existiera, mi gestor de bases de datos probablemente sería postgresql. Sin Joomla! utilizaría Drupal. Sin Zend Framework usaría Codeigniter.

Y sin PHP… ¿¡qué haría yo sin PHP!? ¿Usar Python? Sí, pero noslomismo:) A PHP le tengo un cariño especial. Mi primera página web la hice ya en PHP, en el año 2003, y desde entonces no he parado de aprender.

Bien, el proyecto ya está elegido. Ahora, ¿qué puedo hacer yo por PHP? Ya quisiera yo tener nivel para poder aportar algo al core de PHP. Hoy por hoy donde creo que puedo ser más útil es traduciendo la documentación a español. De modo que me puse en contacto con Yago (yago[arroba]php.net), quien amablemente me indicó los pasos a seguir para formar parte del equipo de traducción.

Tras un breve training, consistente en traducir un par de documentos y emplear la herramienta oficial de traducción, ya estoy dentro. ¡Y muy ilusionado!

Por ahora ya he detectado unas cuantas ventajas:

  • Es agradecido ver que tu trabajo puede ayudar a otras personas.
  • Devuelves parte del favor que otros te han hecho al usar software libre.
  • Aprendes a trabajar en proyectos libres.
  • Puedes fardar de tener una cuenta @php.net (chuso[arroba]php.net).
  • En muchas empresas valoran que participes en proyectos libres.
  • Y lo más importante en mi opinión: Es una forma sensacional de no parar de aprender nuevas cosas de PHP.

¿Alguien más se anima? Actualmente la documentación española es la tercera más traducida, lejos de la japonesa y la francesa. ¡Toda ayuda será bienvenida!

Google ya indexa SVG, mientras IE9 mejora su implementación

SVG, el estándar del w3c para gráficos vectoriales, está dando mucho de qué hablar. Hasta ahora su uso no ha sido muy extendido, e incluso muchos diseñadores y programadores web ni siquiera lo conocen. Es posible que esto cambie poco a poco.

Ejemplo en SVG

Estos días ha habido dos importantes novedades que seguro ayudarán a impulsar esta tecnología.

Google ahora indexa SVG

Por una parte, desde hoy Google indexa los ficheros SVG, tanto si está empotrado dentro de un documento HTML como si está en su propio fichero. Podemos ver ejemplos de cómo funcionan estas búsquedas: Ejemplo 1 y Ejemplo 2. De modo que ya cuentan con un tipo de fichero más a sumar a su extensa lista.

Una de las ventajas más evidentes es que esto impulsará el uso de SVG respecto a Flash. Recordemos que una de las principales funcionalidades de Flash es poder emplear gráficos vectoriales y animaciones en la web, cosa que también se puede realizar con SVG y JavaScript. Salvo que en el caso de Flash, la indexación de contenidos siempre ha sido bastante pobre. Esto con SVG parece que no pasará :)

IE9 se hace amigo de SVG

Y la segunda noticia viene de mano de Microsoft. Precisamente ayer publicaban en el IEBlog un artículo animándonos a que nos preparemos para SVG. A la vista de los resultados, han hecho una estupenda implementación de este estándar en su próximo navegador. Muestran además cómo se comporta, además de algunas pruebas en distintos navegadores.

Es de agradecer también que publiquen ejemplos de uso. No puedo evitar recordar mis inicios con la informática, cuando dibujaba polígonos en Basic o indagaba con la aplicación de renderizado 3d POV-Ray. Afortundamente, contamos con fantásticas aplicaciones para dibujar nuestros gráficos en SVG.

Recordemos que IE9 no saldrá hasta finales de 2010, a pesar de que este mes veremos la primera beta.

Variables en PHP – zval

Todos los lenguajes de programación permiten almacenar valores en memoria. Estos valores generalmente pueden ser de varios tipos -numéricos, caracteres, booleanos, etc. Muchos lenguajes son de tipado estático, es decir, cuando se inicializa una variable, se declara también el tipo de dato que se va a alojar en ella; es el caso de lenguajes como C o Java. Por contra, tenemos también el tipado dinámico, donde se decide en tiempo de ejecución qué clase de dato contendrá la variable; es el caso de PHP o JavaScript.

Tipos de datos en PHP

PHP en concreto soporta los siguientes tipos:

  • Escalares
    • booleano
    • integer
    • float (o double).
    • string
  • Compuestos
    • Array
    • Object
  • Especiales
    • resource
    • NULL

Si en un momento dado quisiéramos mostrar el tipo de una variable, podemos utilizar la función gettype(). Por ejemplo:

<?php
   $a = 'hola';
   $b = 42;
   $c = true;

   echo gettype($a); // Devuelve: string
   echo gettype($b); // Devuelve: integer
   echo gettype($c); // Devuelve: boolean
?>

Para comprobar si una variable es de un tipo determinado usaríamos is_type(), por ejemplo, is_integer($a).

Por otra parte, si lo que quisiéramos es mostrar tanto el tipo como el valor de una variable, utilizaríamos la función var_dump().

Además, al ser PHP un lenguaje de tipado dinámico, podemos forzar en tiempo de ejecución la conversión de un tipo a otro, técnica conocida como casting. Para ello podemos utilizar dos métodos: (1) O bien utilizado un cast, (2) o bien mediante la función settype().

A todo esto, hay que sumarle que PHP también puede cambiar el tipo de una variable en tiempo de ejecución dependiendo de su contexto. Por ejemplo, si a un string le sumamos un entero, PHP convertirá al string en entero en tiempo de ejecución. Esto puede desencadenar en comportamientos inesperados.

Como nota curiosa… Hace poco, en una entrevista de trabajo, me hicieron una pregunta. Si mal no recuerdo era algo tal que así: Si tenemos una variable a la que le asignamos la cadena “abc123″, y la comparamos con el operador == con el entero 0, ¿qué devuelve? La respuesta, aunque sorprendente, es que el resultado es true. Esto se debe a que PHP detecta que en este contexto la cadena debe comportarse como un entero, por lo que convierte “abc123″ en un número: 0. ¿Es 0 igual a 0? Sí, por tanto el resultado el true.

zval

Pero volvamos a las variables. ¿Cómo se almacenan internamente? Cada una de ellas se almacena en un contenedor llamado ‘zval’. Un contenedor zval tiene:

  1. Tipo de la variable. Como hemos visto, PHP tiene 8 tipos primitivos de datos.
  2. Valor de la variable. Esto es, el valor que le hemos asignado.
  3. refcount. Campo numérico que lleva la cuenta del número de variables que utilizan este contenedor.
  4. is_ref. Se trata de un booleano que indica si la variable está accedida por referencia.

Los dos primeros campos están bastante claros, de modo que centrémonos en los dos últimos.

refcount

Hemos visto que el campo refcount lleva la cuenta de las variables que utilizan un determinado zval. Veámoslo con un ejemplo.

<?php
   $a = "test";
   xdebug_debug_zval($a);
   // a: (refcount=1, is_ref=0)='test'
?>

Nota: Para poder consultar el zval de una variable, tan sólo debemos llamar a la función xdebug_debug_zval() de la extensión Xdebug para PHP.

Vemos que el campo refcount está a 1, es decir, sólo hay una variable que utilice este contenedor.

Veamos un ejemplo más avanzado:

<?php
   $a = 'test';
   $b = $a;
   xdebug_debug_zval( 'a' );
   // a: (refcount=2, is_ref=0)='test'

   $b += 'ing';
   xdebug_debug_zval( 'a' );
   // a: (refcount=1, is_ref=0)='test'
?>

¿Qué ha pasado ahora? Vayamos paso a paso. En un primer lugar, creamos la variable $a a la que asignamos el valor ‘test’. Posteriormente creamos otra variable, $b, a la que le copiamos el mismo contenido que tiene $a. Al mostrar el zval de $a vemos que el valor refcount tiene ahora un 2. Esto es porque este contenedor zval está siendo utilizado por dos variables: $a y $b. Nótese que anteriormente he resaltado el verbo copiar, pues $b es una variable distinta a $a. Pero si son dos variables distintas, ¿por qué utilizan el mismo contenedor? Porque PHP es muy inteligente :) Al tener las dos variables el mismo valor, hace que utilicen el mismo contenedor para ahorrar espacio en memoria. ¿Pero qué ocurre si modificamos ahora una de ellas? En el siguiente paso vemos cómo modificamos $b, añadiéndole el sufijo ‘ing’. PHP, que como digo es muy inteligente, ve que ya no tienen el mismo valor $a y $b, por lo que, ahora sí, crea un nuevo espacio en memoria para $b. Por esa razón si ahora vemos el zval de $a comprobaremos que su refcount está a 1.

Este valor es muy útil para la gestión de memoria de PHP. Este lenguaje, a diferencia de otros, contiene un recolector de basura, un proceso que se encarga de liberar la memoria de las variables y objetos que no se van a volver a usar. De este modo, el propio lenguaje se encarga por nosotros de limpiar la memoria. Por supuesto, el recolector de basura liberará todos los contenedores zval cuyo refcount esté a 0, pues significa que no están siendo utilizados por ninguna variable.

En cualquier momento podemos, además, forzar la eliminación de una variable, llamando a la función unset(). Ésta lo que hace es decrementar el valo refcount. Y como hemos visto, si llega a 0, se eliminará por completo el zval correspondiente.

is_ref

Si alguna vez has programado en C, sabrás bien lo que son los punteros: Se trata de variables cuyo valor es una dirección de memoria física, de modo que únicamente apuntan al lugar donde se encuentra su valor. Esto es muy útil para, por ejemplo, hacer que varias variables sean en realidad la misma. O para pasar a una función una variable, y que la función pueda modificar el valor de ésta. Esto es posible en PHP utilizando referencias.

Nota: A partir de PHP5 cada vez que pasas un objeto a una función o método, éste se pasa siempre por referencia. Es decir, las modificaciones que se hagan de un objeto dentro de una función persistirán fuera de ésta.

Veamos dos ejemplos para comprender que son las variables por referencia:

<?php
   $a = 'hello';
   $b = $a;
   echo $b; // Muestra 'test';
   $b = 'ciao';
   echo $a; // Muestra 'test';
   echo $b; // Muestra 'ciao';
?>

Simplemente copiamos a $b el valor de $a. Posteriormente modificamos $b, y comprobamos cómo sólo ha cambiado éste, pero no $a. El siguiente ejemplo es prácticamente igual, salvo que en lugar de copiar $a a $b, copiamos por referencia:

<?php
   $a = 'hello';
   $b =& $a;
   echo $b; // Muestra 'test';
   $b = 'ciao';
   echo $a; // Muestra 'ciao';
   echo $b; // Muestra 'ciao';
?>

En este caso, al haber usado el operador =&, hemos hecho que $b sea en realidad la misma variable que $a. Y aquí es donde volvemos con zval. ¿Qué contiene ahora? Comprobémoslo:

a: (refcount=2, is_ref=1)='ciao'

¡Y ahí lo tenemos! Ahora el campo is_ref tiene valor true (1). Esto es porque, como hemos visto, este zval está siendo también apuntado por una variable por referencia. Además podemos comprobar cómo el refcount se ha incrementado a 2.

zval en tipos compuestos

Hasta ahora hemos visto ejemplos de zval en variables escalares. Vamos a ver un ejemplo de qué se almacena cuando es un tipo compuesto, como por ejemplo un array:

<?php
   $a = array('foo' => 'bar', 'gen' => 'oma');
   xdebug_debug_zval('a');
?>

Y esto es lo que obtenemos:

a: (refcount=1, is_ref=0)=array (
‘foo’ => (refcount=1, is_ref=0)=’bar’,
‘gen’ => (refcount=1, is_ref=0)=’oma’
)

¡Curioso! Un array lo que hace es crear una nueva tabla, distinta de la global, de valores zval. Pero la cosa se puede complicar bastante. Para terminar, vamos a ver el siguiente ejemplo extraído de la documentación de PHP:

<?php
   $a = array( 'one' );
   $a[] =& $a;
   xdebug_debug_zval( 'a' );
?>

Creamos un array con un solo elemento. Posteriormente creamos un segundo campo en el que asignamos una referencia circular al propio array. Obtenemos el siguiente resultado:

a: (refcount=2, is_ref=1)=array (
   0 => (refcount=1, is_ref=0)='one',
   1 => (refcount=2, is_ref=1)=...
)

Supongamos que ahora hacemos una llamada a unset($a). El resultado esperado sería que se eliminara por completo de memoria el zval anterior. ¿Pero es esto lo que realmente ocurre? Como explicamos anteriormente, mediante unset() lo que hacemos es decrementar el refcount. Pero en este caso, tras llamar a unset() quedaría el siguiente zval huérfano sin ser eliminado de memoria pues su refcount vale 1:

(refcount=1, is_ref=1)=array (
   0 => (refcount=1, is_ref=0)='one',
   1 => (refcount=1, is_ref=1)=...
)

No se elimina puesto que el item 1 del array sigue apuntando al propio array. Y al no haber ningún símbolo externo apuntando a este zval, no existe ningún mecanismo que nos permita liberar esta memoria, obteniendo un memory leak. Afortunadamente, desde PHP 5.3, esta memoria se libera automáticamente al finalizar la petición. Pero hasta entonces, sigue ocupando memoria, lo cual puede ser un problema en procesos de larga duración como por ejemplo un demonio.

Resumen

En este artículo hemos pasado de lo más elemental, hasta conocer cómo se almacenan las variables internamente en PHP, y sus mecanismos de control. Conocemos los 8 típos primitivos de PHP, y cómo trabajar entre ellos. Sabemos qué es el campo refcount, lo que son los valores por referencia, el campo is_ref, y cómo afectan éstos al recolector de basuras. Del mismo modo, hemos visto cómo hay situaciones que pueden escapar a nuestro control, donde la memoria no queda libre a pesar de haber finalizado una variable.

Para más información podéis consultar:

Programación basada en prototipos

En los resúmenes de los distintos paradigmas de programación se suele mencionar la programación funcional, lógica, procedural, orientada a objetos…

Dentro de la programación orientada a objetos, hay distintos tipos. El más conocido es el basado en clases, donde encontramos lenguajes como Java, C++, C#… Sin embargo, hay más tipos de programación orientada a objetos, y es usada por lenguajes muy conocidos. Me estoy refiriendo a la programación basada en prototipos, y su lenguaje más conocido es JavaScript.

Según la Wikipedia: Programación basada en prototipos es un estilo de programación orientada a objetos en el cual, las “clases” no están presentes, y la re-utilización de procesos (conocida como herencia en lenguajes basados en clases) se obtiene a través de la clonación de objetos ya existentes, que sirven de prototipos, extendiendo sus funcionalidades.

Es decir, se trata de un paradigma orientado a objetos sin clases. ¿Cómo es esto? En lugar de definir una clase, para después crear una instancia de ella, lo que hacemos es definir directamente el nuevo objeto, con sus métodos y atributos. En la página web de Mozilla tenemos un buen resumen de las diferencias existentes entre la POO basada en prototipos y la basada en clases.

Una guía muy buena para entender, paso a paso, en qué consiste, y cómo funciona, es la que proporciona de nuevo Mozilla en su artículo “A re-introduction to JavaScript“, en el apartado “Custom Objects“. Al no estar en español, voy a copiarlos aquí:

Consideremos un objeto persona con los campos nombre y apellido. Hay dos formas de mostrar: “nombre apellido” o “apellido, nombre”. Esta es una forma de hacerlo:

function makePerson(first, last) {
    return {
        first: first,
        last: last
    }
}
function personFullName(person) {
    return person.first + ' ' + person.last;
}
function personFullNameReversed(person) {
    return person.last + ', ' + person.first
}

Esto funciona, pero es bastante feo. Terminas con docenas de funciones y tu espacio de nombres global. Lo que realmente necesitamos es una forma de vincular una función a un objeto. Puesto que las funciones son objetos, esto es fácil:

function makePerson(first, last) {
    return {
        first: first,
        last: last,
        fullName: function() {
            return this.first + ' ' + this.last;
        },
        fullNameReversed: function() {
            return this.last + ', ' + this.first;
        }
    }
}

Podemos aprovechar la palabra clave ‘this‘ para mejorar nuestra función makePerson:

function Person(first, last) {
    this.first = first;
    this.last = last;
    this.fullName = function() {
        return this.first + ' ' + this.last;
    }
    this.fullNameReversed = function() {
        return this.last + ', ' + this.first;
    }
}

Nuestro objeto persona ha mejorado, pero aun tiene algunos flecos feos. Cada vez que creamos un objeto persona, estamos creando dos veces las funciones que hay en cada uno – ¿no sería genial si pudieran compartir este código?

function personFullName() {
    return this.first + ' ' + this.last;
}
function personFullNameReversed() {
    return this.last + ', ' + this.first;
}
function Person(first, last) {
    this.first = first;
    this.last = last;
    this.fullName = personFullName;
    this.fullNameReversed = personFullNameReversed;
}

Esto está mejor: estamos creando las funciones sólo una vez, y asignando referencias a ellas dentro del constructor. ¿Podemos hacerlo todavía mejor? La respuesta es sí:

function Person(first, last) {
    this.first = first;
    this.last = last;
}
Person.prototype.fullName = function() {
    return this.first + ' ' + this.last;
}
Person.prototype.fullNameReversed = function() {
    return this.last + ', ' + this.first;
}

Person.prototype es un objeto compartido por todas las instancias de Person. Forma parte de una cadena de búsqueda (que tiene un nombre especial, “prototype chain”): cada vez que intentas acceder a una propiedad de Person que no está asignada, JavaScript comprobará en Person.prototype para ver si esta propiedad existe ahí. Como resultado, cualquier cosa que asignemos a Person.prototype estará disponible en todas las instancias de ese constructor mediante el objeto this.

Ahora, si os pasaba como a mí, ya tendréis claro qué es eso que hace tan “rarito” a JavaScript. Y se trata ni más ni menos de su forma de ser orientado a objetos.

Historial y botón volver en JavaScript

Cada día son más frecuentes las aplicaciones web que hace un uso extensivo de JavaScript, relegando una mayor carga computacional al cliente, dentro de la arquitectura cliente-servidor, lo que habitualmente se conoce como cliente pesado. Con ello, entre otras cosas, se logra una interfaz de usuario mucho mas rica en prestaciones.

Algunos ejemplos de webs que hacen uso de esto es el cliente de correo de Google Gmail, o las famosas redes sociales como Facebook o Tuenti. En estas aplicaciones, toda la interfaz está gobernada por JavaScript. Cuando accedemos a un enlace, generalmente no conlleva una nueva petición HTTP sino que, en su lugar, el documento actual se actualiza con nuevo contenido (AJAX).

Pero existe un inconveniente: Al hacer las webs así, no se almacenan las actualizaciones de la página en el historial del navegador, de modo que los botones de volver y avanzar del navegador no funcionan como esperaríamos. Esto ocurre porque la URL no ha cambiado, y por tanto, para el navegador no ha habido ningún cambio de página.

Pero en Facebook o GMail, pese a funcionar con JavaScript, sí funciona el botón de volver. ¿Cómo lo hacen? La magia está en el caracter almohadilla (#). Repasemos HTML: Cuando en un documento queremos hacer un enlace a una parte del mismo (Por ejemplo, para saltar a un apartado), lo hacemos con el caracter almohadilla seguido de la palabra clave. Por ejemplo:

<h1 id="top">Inicio</h1>
<p>Un párrafo con mucho contenido...</p>
<a href="#top">Volver arriba</a>

Una de las ventajas de estos enlaces es que no conllevan una nueva petición HTTP, por lo que son inmediatos. Además, el navegador tiene constancia de ellos, por lo que los almacena en el historial. Bien, pues ahí está el truco. Si nos fijamos en una URL de Facebook, veremos que son de este tipo:

http://www.facebook.com/#!/?sk=messages&filter=[fb]unread

Es decir, en este caso el enlace es a la página principal (http://www.facebook.com/) y dentro de ese documento, a un apartado (#!/?sk=messages&filter=[fb]unread). Con JavaScript podemos consultar ese apartado para, en función de él, cargar el contenido que proceda. Ya tenemos todas las piezas para hacer una aplicación web en JavaScript, donde funcionará el historial del navegador, y además podremos guardar las URLs en nuestros marcadores.

Acerca del evento hashchange

Para poder realizar eso, necesitamos un método para que, desde JavaScript, sepamos cuándo se ha actualizado el historial #hash, es decir, para saber cuándo se enlaza a otro apartado dentro del documento actual. En HTML5 queda resuelto esto con el evento windows.onhashchange. Sin embargo, al ser un evento nuevo, no está disponible en todos los navegadores, por lo que se hacen necesarios algunos hacks que permitan que nuestra aplicación funcione en la mayoría de navegadores.

En el caso de utilizar jQuery, el plugin más extendido es jQuery BBQ. Podemos leer más acerca de cómo funciona aqui:
http://benalman.com/projects/jquery-hashchange-plugin/

Implementación básica en jQuery

Este ejemplo es muy sencillo. Únicamente tendremos una lista de enlaces, y marcaremos en negrita al activo, y mostraremos su contenido. Entendiendo eso, podremos aplicarlo a todo lo demás.

En primer lugar, necesitaríamos incluir la biblioteca jquery, y además también el plugin de jquery BBQ.

<!DOCTYPE html>
<html lang="es">
<head>
<script type="text/javascript" src="jquery-1.4.1.js"></script>
<script type="text/javascript" src="jquery.ba-bbq.js"></script>
</head>
<body>
</body>
</html>

Después definimos una lista de enlaces y sus bloques de contenido:

<ul>
    <a href="#enlace1">Enlace 1</a>
    <a href="#enlace2">Enlace 2</a>
    <a href="#enlace3">Enlace 3</a>
    <a href="#enlace4">Enlace 4</a>
</ul>
<div id="enlaces">
    <div id="enlace1">Contenido Enlace 1</div>
    <div id="enlace2">Contenido Enlace 2</div>
    <div id="enlace3">Contenido Enlace 3</div>
    <div id="enlace4">Contenido Enlace 4</div>
</div>

Y ahora el código JavaScript:

$(function(){
  $(window).bind('hashchange', function(e) {
    var url = $.param.fragment();
    $('a.current').removeClass('current');
    if (url) {
        $('a[href="#' + url + '"]').addClass( 'current' );
        $("#enlaces > div").hide();
        $("#"+url).show();
    }
  });

  $(window).trigger( 'hashchange' );

});

El código hace lo siguiente:

  1. En primer lugar definimos una función que irá atada al evento ‘hashchange’. Este evento se dispara, como hemos visto, cada vez que se actualiza el #hash.
  2. En segundo lugar, consultamos el parámetro #hash y lo almacenamos en la variable ‘url’.
  3. Desactivamos el enlace previamente activo.
  4. En caso de que esté definido el parámetro #hash:
    1. Activamos el nuevo enlace activo. Tan sólo le añadimos una clase.
    2. Ocultamos todos los bloques de contenido.
    3. Mostramos el bloque de contenido activo.
  5. Lanzamos el evento hashchange para provocar una actualización, por si en la primera carga de la página ya hubiera un parámetro #hash (Por ejemplo, porque alguien ha guardado una URL en favoritos).

Finalmente, definimos el estilo para la clase “current” y ya lo tenemos.

Podéis probar una demo. Como veis, tras moverte entre los distintos apartados, los botones de Volver y Avance del navegador funcionan. Además, se pueden almacenar las URLs para su futura consulta.