PHP-TUT-18 La comunicación web

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail

En este artículo vamos a conocer algunas prestaciones de PHP que, por su propia naturaleza, son especialmente aptas para su integración en comunicaciones web (HTTP, FTP, etc.). En realidad, no es ninguna sorpresa, ya que PHP es un lenguaje de script para trabajar en una arquitectura cliente-servidor. Sin embargo, los conceptos que vamos a conocer aquí, aún con ser muy simples, nos resultarán extremadamente útiles para integrarlos en algunas de las páginas que desarrollemos en nuestra trayectoria profesional. En este artículo conocerás más variables y matrices propias de PHP, y entenderás el cómo y el por qué de algunos conceptos que hemos avanzado en anteriores artículos.

LAS CABECERAS

Cuando se navega por Internet (así como cuando se usa el correo electrónico, o se suben programas a un servidor, etc.) existe una doble comunicación. En primer lugar está la solicitud, cursada por el cliente al servidor. Así, cuando te conectas a cualquier página de Internet, tu navegador envía un requerimiento al servidor para que te proporcione los contenidos de dicha página. El servidor escucha tu petición y, si es posible, te remite los contenidos solicitados. Es la segunda comunicación, llamada respuesta. Siempre es de ese modo. Primero se produce una solicitud por parte del cliente, seguida de una respuesta por parte del servidor. A continuación, se cierra la comunicación. Es decir, el servidor, cuando ha enviado el recurso solicitado al cliente, cierra la comunicación, hasta que el cliente haga otra solicitud. Por eso HTTP es un protocolo sin estados. Ésta es la principal diferencia operacional entre una aplicación web y un programa de usuario. Y por esta razón son necesarias las técnicas que vimos en el artículo anterior cuando hay que pasar valores de una página a otra. En cada comunicación (solicitud y respuesta) existen dos partes claramente diferenciadas: la cabecera y el cuerpo o contenido del mensaje. Ambas se separan con una línea en blanco, razón por la cual no puede haber líneas sin contenido en la cabecera. De las cabeceras nos vamos a ocupar en este apartado.

Antes de seguir adelante vamos a aclarar el concepto de cabecera. En las solicitudes, la cabecera contiene datos acerca del navegador del cliente, el sistema operativo, etc. El cuerpo del mensaje es la solicitud en sí que el cliente ha efectuado. El cuerpo de la solicitud es lo que se conoce como un URI (Universal Resource Identifier, Identificador Universal de Recursos). El URI puede estar formado por la URL del sitio al que queremos acceder y, en su caso, por la ruta. Además, puede haber pares nombre-valor a continuación. Por ejemplo, supongamos que accedes a una página desde un formulario enviado mediante GET. En la barra de direcciones de tu navegador podría aparecer algo como lo siguiente:

http://www.misitio.com/main.php?nombre=Juan&apellido=Garrido

Todo lo que hay a continuación del signo ? se conoce como Query String, o Cadena de la Consulta. Recuerda que una solicitud se genera cada vez que se le pide algo al servidor. Esto significa no sólo que escribas algo en la barra de direcciones. También es una solicitud cuando envías un formulario o pulsas un enlace. En las respuestas del servidor (cuando se le envía la página solicitada), el término cabecera no se refiere a la sección <head></head> que hay en cualquier página web. Esto forma parte del contenido del mensaje. La cabecera son unos datos previos a la propia página en sí, que informan al navegador de si se ha podido cursar la petición, la fecha, el tipo de documento, etc.

CABECERAS DE SOLICITUD

Vamos a empezar viendo las cabeceras de una solicitud para familiarizarnos con algunos conceptos. Para ello usaremos la función getallheaders(), que no recibe ningún argumento. Esta función recupera las cabeceras de la solicitud en una matriz asociativa. En cada elemento de esta matriz el nombre es el de una de las líneas de contenido de la cabecera y el valor es el que tiene asignada dicha línea. Para comprender mejor esto veamos el listado cabecerasSolicitud.php:

Observa, en la línea resaltada, el uso de la función que estamos estudiando. Al cargar este script en tu navegador obtendrás un resultado similar al siguiente (puede variar dependiendo de tu navegador).

Cabeceras de la solicitud

Cabeceras de la solicitud

En la tabla que obtenemos vemos las cabeceras que tiene esa solicitud en concreto. Los nombres de las mismas, así como su significado aparecen en la tabla reproducida a continuación:

CABECERA SIGNIFICADO
Accept Muestra una lista con los tipos de recursos que el navegador es capaz de gestionar y mostrar al usuario, es decir, texto, imágenes, clips de vídeo y/o audio, animaciones, etc. En el ejemplo obtenemos, entre otros detalles, */*. Eso quiere decir que el navegador que estoy usando tiene todos los plugins necesarios para gestionar cualquier contenido de hipermedia.
Accept-Language Muestra el idioma en el que está configurado el navegador.
Accept-Encoding Muestra los tipos de codificación soportados.
User-Agent Muestra información acerca del navegador y el sistema operativo.
Host El nombre del servidor al que nos conectamos.
Connection El estado de la conexión. Se mantiene abierta (keep-alive) porque en el momento de generarse esta matriz, la página todavía no ha sido enviada al navegador. Cuando lo sea, será el servidor quien cierre la conexión.

Éstas son sólo algunas de las cabeceras que pueden preceder a la solicitud. En un artículo anterior vimos una función de PHP que da información muy completa acerca de la instalación del intérprete en nuestro servidor. Se trata de phpinfo(). En aquel momento no entramos en detalles acerca de qué era lo que estábamos viendo. Carga el script http://localhost/primera_prueba/index.php (recuerda que debes tenerlo en la carpeta que has definido como servidor local. Revisa el artículo mencionado si dudas). Una vez cargado busca el apartado HTTP Headers Information y verás una doble tabla con una lista de las cabeceras que hay.

Si sólo deseas recuperar una o dos cabeceras concretas de tu solicitud, puedes usar algunas de las variables propias del intérprete para ello, en lugar de la función getallheaders(). Observa el listado de algunasCabeceras.php:

En las líneas resaltadas ves cómo hemos hecho uso de dos de las variables del lenguaje para recuperar sendas líneas de contenido de la cabecera de petición, a través de la matriz $_SERVER, propia de PHP.

En general, si deseas recuperar alguna cabecera mediante este sistema, el nombre de la variable es $_SERVER["HTTP_CABECERA"], donde HTTP_CABECERA es el nombre de la cabecera en la que estás interesado.

CABECERAS DE RESPUESTA

Las cabeceras de la respuesta son enviadas por el servidor al navegador e interpretadas por este. Así pues, lo primero que intuimos es que nos ofrecen ciertas posibilidades de manipular el comportamiento del navegador. No te asuste. Esto no quiere decir que nadie pueda usar las cabeceras de la respuesta con fines maliciosos, tales como anular el antivirus, espiar contenidos del disco del cliente, ni nada así. Son manipulaciones totalmente inofensivas, destinadas a mejorar, o no, el comportamiento del navegador. Por ejemplo, tengamos en cuenta la primera línea de cabecera que se envía con la respuesta. Tiene el siguiente formato genérico:

Protocolo CódigoDeEstado Descripción

Por ejemplo, una cabecera típica es la siguiente:

HTTP/1.1 200 OK

La primera parte muestra que la comunicación es en HTTP 1.1, que es la versión actual del protocolo. A continuación, se recibe el código de estado, que es un número de tres cifras y que se refiere al resultado de la respuesta. El código 200 le indica al navegador que la respuesta ha sido correctamente enviada por el servidor. Esto lo vemos en la descripción del código (OK). Cuando el navegador recibe este código de estado (el 200), “sabe” que el documento ha sido correctamente enviado y lo muestra.

Recuerda que la función header() debe ser llamada antes que cualquier salida, ya sea mediante etiquetas HTML normales, líneas en blanco de un archivo, o desde PHP. Es un error muy común insertar código externo con include() o require() y terminar con espacios o líneas en blanco que son impresas antes de una llamada a header(). El mismo problema existe cuando se usa un archivo PHP/HTML único.

Los códigos de estado HTTP determinan el comportamiento inicial del navegador. Estos códigos tienen siempre tres cifras y se agrupan dependiendo de la primera, según la siguiente tabla:

CÓDIGOS DE RESPUESTA HTTP
CÓDIGO  SIGNIFICADO
1xx Informativo.
101 Se ha producido un cambio de protocolo.
2xx Envío realizado con éxito.
200 Envío completado sin errores (es el código que acompaña a un resultado exitoso habitual).
204 Sin contenido.
3xx Redireccionamientos.
302 Redirección a otro documento.
4xx Error causado por el cliente.
401 No está autorizado a ver esta página.
403 Contenido prohibido.
404 No se ha encontrado la página.
407 Se requiere un proxy.
408 Tiempo de espera agotado.
414 URI de solicitud demasiado largo.
5xx Error causado por el servidor.
500 Error Interno.
503 Servicio no disponible.
505 Versión de HTTP no soportada.

Existen algunos otros códigos, pero los que aparecen aquí son los que más habitualmente se generan.

La función header() proporciona más posibilidades. Por ejemplo, supongamos que has mudado tu sitio a otro dominio. Antes lo tenías bajo el nombre, digamos, www.sitioantiguo.com, y ahora está en www.sitionuevo.com. Así pues, deseas que los usuarios que no sepan del cambio y pretendan llegar a tus contenidos a través del dominio antiguo, sean redirigidos, automáticamente, al nuevo sitio. Para ello el fichero index.php del alojamiento del sitio antiguo será sustituido por las siguientes líneas:

<?php
    header("Location: http://www.sitionuevo.com/");
?>

Existen un par de cabeceras con las que es muy interesante contar cuando programamos páginas dinámicas. En estas páginas es habitual que los contenidos se actualicen con frecuencia. Eso puede suponer una interferencia con el caché de los navegadores, que pueden estar mostrándole al usuario una información caduca. Esto lo solucionamos con las siguientes líneas de cabecera al principio del script.

header ("Cache-Control: no-cache, must-revalidate");
header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT");

La primera línea le dice al navegador que no atienda al caché y cargue la página directamente desde el servidor. La segunda establece una fecha, que debe ser anterior a la actual, como momento de expiración de la página. De este modo, se recoge todo el contenido procedente del servidor.

SOCKETS

En el primer artículo de esta serie supimos, a grandes rasgos, lo que es un socket, desde el punto de vista de la pila de protocolos TCP/IP. Ahora vamos a saber cómo podemos manejar un socket desde nuestra página. La idea es conectarnos a un equipo que tiene asignada una dirección IP determinada, a través de un puerto concreto, y poder leer su contenido, o escribir en él. La forma de abrir, usar y cerrar un socket es muy similar a como hacíamos con los ficheros del disco (mira el artículo al respecto). La principal diferencia está en que, para abrir un socket, usamos la función fsockopen(), en lugar de fopen(). Esta función recibe dos argumentos, separados por una coma. El primero es el nombre del recurso al que queremos acceder y el segundo es el puerto por el que esperamos que el servidor esté escuchando. La función devuelve un manejador que luego usaremos como hacemos con los ficheros Eso, suponiendo que el socket se abra correctamente. Si no es así, la función devuelve un valor false. Por ejemplo, si queremos acceder a localhost, emplearemos la siguiente sintaxis:

$manejador = fsockopen("127.0.0.1", 80);

Esto nos permitirá conectarnos al servidor local por el puerto 80, que es el que tenemos configurado para escuchar solicitudes HTTP.

Otra alternativa es indicar el nombre de dominio del recurso al que nos queremos conectar. Observa el siguiente ejemplo:

$manejador = fsockopen("www.misitio.com", 80);

Este método presenta un pequeño inconveniente. Antes de establecer la conexión, el script debe resolver el nombre, es decir, conectarse al servidor DNS y determinar cuál es la dirección IP del dominio indicado. Por esta razón es, técnicamente, más lento aunque eso es algo que, con las máquinas y líneas actuales, tampoco vas a notarlo.

Las demás funciones, tanto de lectura como de escritura y cierre del socket, son las mismas que se usan para los ficheros.

Vamos a ver un ejemplo interesante. A lo largo de este artículo has aprendido a ver el contenido de las cabeceras de la solicitud en la página, con la función getallheaders() y a “forzar” algunas cabeceras de respuesta, con header(). Vamos a usar un socket para lograr recuperar las cabeceras de repuesta de una página, ignorando el contenido. Observa el listado del script cabecerasRespuesta.php:

Quiero que prestes especial atención a la línea que abre el socket, y que aparece resaltada en el listado. Fíjate en que, tal como hemos dicho, en la función fsockopen() aparece, en primer lugar, el nombre del recurso que queremos abrir. Como, en este caso, se trata del servidor local y ya conocemos su IP, la tecleamos directamente. En segundo lugar aparece el puerto por el que queremos establecer la comunicación. Como vamos a trabajar con el protocolo HTTP, indicamos el valor 80. Si fuéramos a trabajar con el protocolo SMTP, por ejemplo, pondríamos el valor 25. Cada protocolo trabaja en un puerto determinado.

A continuación vemos una línea que escribe una solicitud, mediante fputs. Como ya he comentado, los sockets, una vez abiertos, se tratan como si fueran ficheros. Como nuestro objetivo es obtener las cabeceras de la respuesta, es necesario que primero se presente una solicitud.

En ese momento se empieza a leer la respuesta. Mira los comentarios incluidos en el código para entender cómo la procesamos.

Veamos qué hemos obtenido en el navegador.

  • La primera línea nos informa de que la transmisión por HTTP ha sido redireccionada (código de estado 302). Esto ocurre por la forma en que se accede al servidor si tenemos conectado un proxy actuando como local (por ejemplo, una VPN o similar).
  • La segunda línea informa de la fecha y hora del servidor, no del cliente. No olvide que estamos recuperando las cabeceras de respuesta. Cómo estamos en localhost, coincidarán estos datos con los de tu ordenador.
  • La tercera línea informa de cuál es el software del servidor, la plataforma sobre la que estamos trabajando y la versión del intérprete de PHP.
  • La cuarta línea es un poco redundante, puesto que nos da, una vez más, la versión del intérprete.
  • La quinta línea es una referencia a la propia plataforma. Puedes ignorarla.
  • La sexta te da la longitud total de las cabeceras.
  • La séptima línea es la que nos informa de que la conexión está cerrada. Recuerda lo que le he comentado anteriormente, acerca de que, una vez enviada la respuesta, el servidor corta la conexión, hasta que se produce la siguiente solicitud.
  • Por último, la última línea informa del tipo de contenido de la página.
     

4 comentarios:

  1. Pingback: PHP-TUT-20 Gestión de imágenes (I) » eldesvandejose.com

  2. I have been browsing online more than three hours nowadays, yet I by no means found any attention-grabbing article like yours.
    It’s beautiful value sufficient for me. Personally, if all website owners and bloggers made
    just right content as you probably did, the web can be much more helpful than ever before.

  3. Hi there! I know this is kind of off topic but I was wondering which
    blog platform are you using for this site?

    I’m getting sick and tired of WordPress because I’ve had problems with hackers and I’m looking at alternatives for another platform.

    I would be awesome if you could point me in the direction of a good platform.

    • Hi. I use WordPress for this site. Latest versions are quite safe for the most of the cases. If you are using an older one you should update. Your security troubles could be caused by the theme too. Some older themes ar so vulnerable. However, Drupal is a nice option, since it’s developed thinking on security.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *