Skip to content

10. Servidores en PHP

Dado que los programas PHP pueden ser ejecutados por un servidor WEB, dicho programa se convierte en un programa servidor capaz de atender a varios clients. Desde el punto de vista del cliente, llamar a un servicio WEB equivale a solicitar el URL de dicho servicio. El cliente puede estar escrito en cualquier lenguaje, en particular en PHP. En este último caso, se utilizan las funciones de red que acabamos de ver. Además, debemos saber «comunicarnos» con un servicio WEB, es decir, comprender el protocolo http de comunicación entre un servidor web y sus clients. Este es el objetivo de los programas que siguen.

El cliente web descrito en el apartado 9.2 nos ha permitido descubrir una parte del protocolo HTTP.

Image

En su forma más simple, los intercambios cliente/servidor son los siguientes:

  • el cliente abre una conexión con el puerto 80 del servidor web
  • realiza una solicitud relativa a un documento
  • el servidor web envía el documento solicitado y cierra la conexión
  • el cliente, a su vez, cierra la conexión

El cliente puede ser de diversa índole: un texto en formato HTML, una imagen, un vídeo... Puede tratarse de un documento existente (documento estático) o bien de un documento generado sobre la marcha por un script (documento dinámico). En este último caso, se habla de programación web. El script de generación dinámica de documentos puede estar escrito en diversos lenguajes: PHP, Python, Perl, Java, Ruby, C#, VB.net, etc.

Aquí utilizamos PHP para generar dinámicamente documentos de texto.

  • en [1], el cliente establece una conexión con el servidor, solicita un script PHP, envía o no parámetros a dicho script
  • en [2], el servidor web ejecuta el script PHP mediante el intérprete PHP. Este script genera un documento que se envía al cliente [3]
  • el servidor cierra la conexión. El cliente hace lo mismo.

El servidor web puede procesar varios clients a la vez. Con el paquete de software WampServer, el servidor web es un servidor Apache, un servidor de código abierto de la Apache Foundation (http://www.apache.org/). En las siguientes aplicaciones, se debe iniciar WampServer. Esto activa tres programas: el servidor web Apache, SGBD MySQL y el intérprete PHP.

Los scripts ejecutados por el servidor web se escribirán con la herramienta Netbeans. Hasta ahora hemos escrito scripts PHP ejecutados en un contexto de consola:

El usuario utiliza la consola para solicitar la ejecución de un script PHP y recibir los resultados.

En las aplicaciones cliente/servidor que veremos a continuación,

  • el script del cliente se ejecuta en un contexto de consola
  • el script del servidor se ejecuta en un contexto web

El script PHP del servidor no puede estar en cualquier lugar del sistema de archivos. De hecho, el servidor web busca los documentos estáticos y dinámicos que se le solicitan en las ubicaciones especificadas en la configuración. La configuración predeterminada de WampServer hace que los documentos se busquen en la carpeta <WampServer>/www, donde <WampServer> es la carpeta de instalación de WampServer. Así, si un cliente web solicita un documento D con el URL [http://localhost/D], el servidor web le servirá el documento D de la ruta [<WampServer>/www/D].

En los ejemplos siguientes, colocaremos los scripts del servidor en la carpeta [www/exemples-web]. Si un script de servidor se llama S.php, se solicitará al servidor web con el URL [http://localhost/exemples-web/S.php]. A continuación, se le servirá el documento [<WampServer>/www/exemples-web/S.php].

Para crear un script de servidor con Netbeans, procederemos de la siguiente manera:

  • en [1], creamos un nuevo proyecto
  • en [2], tomamos la categoría [PHP] y el proyecto [PHP Application]
  • en [3], le damos un nombre al proyecto
  • en [4], elegimos una carpeta para el proyecto
  • en [5], indicamos que el script debe ejecutarse mediante un servidor web local (la URL del script tendrá el formato http://localhost/...). El servidor web local será el servidor web Apache de WampServer.
  • En [6], indicamos el URL del proyecto. Aquí decidimos que se solicitará un script S.php del proyecto con el URL [http://localhost/exemples-web/S.php]. Según lo dicho, esto significa que la ruta del script S.php en el sistema de archivos será [<WampServer>/www/exemples-web/S.php]. Esto es lo que se indica en [7]. Aquí solicitamos que cualquier script S.php del proyecto se copie en el árbol de directorios del servidor web Apache.
  • en [8], el nuevo proyecto.

Escribamos un script de prueba:

  • en [1], creamos en el proyecto [exemples-web] un primer script PHP
  • en [2], le damos un nombre
  • en [3], tras crearlo, le asignamos el siguiente contenido

A continuación, es necesario que se ejecute WampServer.

  • en [4], ejecutamos el script web [exemple1.php]. Netbeans iniciará entonces el navegador predeterminado del equipo y le pedirá que muestre el URL [http://localhost/exemples-web/exemple1.php] [5]
  • en [6], el navegador muestra lo que el script del servidor ha escrito al cliente.

A continuación, nos encontraremos con dos tipos de páginas web:

  • un navegador como el anterior. Hemos escrito que el servidor web enviaba una respuesta del tipo: encabezados HTTP, línea vacía, texto. El navegador solo muestra el texto.
  • un script PHP que le mostrará la respuesta completa: encabezados HTTP, línea vacía, texto.

A continuación,

  • los scripts de servidor se escribirán como [exemple1.php] arriba
  • los scripts de cliente se escribirán como los scripts de consola que hemos escrito hasta ahora.

10.1. Aplicación cliente/servidor de fecha/hora

10.1.1. El servidor (web_01)


<?php
// hora: número de milisegundos desde el 01/01/1970
// formato de visualización de fecha y hora
// d: día en dos dígitos
// m: mes en dos dígitos
// y: año en dos dígitos
// H: hora 0,23
// i: minutos
// s: segundos
print date("d/m/y H:i:s",time());

Básicamente, el script PHP anterior muestra la hora actual en la pantalla. Sin embargo, cuando lo ejecuta un servidor web, el flujo n.º 1, que normalmente se asocia a la pantalla, se redirige a la conexión que une al servidor con su cliente. Por lo tanto, en un contexto web, el script anterior envía la hora actual en forma de texto al cliente.

Ejecutemos este script dentro de Netbeans:

  • en [1], se ejecuta el script. A continuación, se inicia un navegador web.
  • En [2], se ejecuta el URL solicitado por el navegador web
  • en [3], el texto enviado por el script del servidor

El navegador cliente utiliza el protocolo HTTP para comunicarse con el servidor web. Ya hemos descrito la forma de este protocolo.

El cliente envía líneas de texto que se pueden descomponer en tres partes: encabezados HTTP, línea vacía, documento. El documento enviado al servidor web suele estar vacío o bien es un conjunto de parámetros parami=vali, donde vali es un valor introducido por el usuario en un formulario Html.

La respuesta del servidor tiene la misma estructura: encabezados HTTP, línea vacía, documento, donde el documento es, en este caso, el documento solicitado por el navegador del cliente. Si el cliente ha enviado parámetros, el documento entregado depende, por lo general, de dichos parámetros.

Con el navegador Firefox, es posible ver los intercambios reales entre el cliente y el servidor web. Existe un complemento para Firefox, llamado Firebug, que permite rastrear estos intercambios. Firebug está disponible en URL [https://addons.mozilla.org/fr/firefox/addon/firebug/]. Si se utiliza el navegador Firefox para visitar esta Url, se puede descargar el complemento Firebug. A continuación, suponemos que el complemento Firebug se ha descargado e instalado. Está disponible a través de una option del menú de Firefox:

 

Se abre una ventana de Firebug en la ventana del navegador Firefox. Esta ventana presenta a su vez un menú:

 

Para ver los intercambios entre el cliente y el servidor durante una solicitud HTTP, solicitamos el URL [http://localhost/exemples-web/web_01.php] con el navegador Firefox. La ventana de Firebug se llena entonces de información:

Arriba tenemos un resumen de los intercambios entre el cliente y el servidor:

  • [1]: el cliente ha enviado la solicitud HTTP: GET /ejemplos-web/web_01.php HTTP/1.1 para solicitar el documento [web01.php]
  • [2]: el servidor ha enviado la respuesta: HTTP/1.1 200 OK indicando que ha encontrado el documento solicitado.

Firebug permite obtener los intercambios completos. Basta con «desplegar» el URL:

Arriba vemos los encabezados Http intercambiados entre el cliente (Solicitud) y el servidor (Respuesta). Es posible obtener el código fuente de los intercambios, c.a.d, y las líneas de texto realmente intercambiadas, [1]. Obtenemos entonces el siguiente código fuente:

 

Para escribir un script de cliente del servidor web, basta con reproducir el comportamiento del navegador. Tras establecer una conexión con el servidor, el script de cliente podría enviar las 8 líneas de la solicitud anterior. De hecho, no todo es imprescindible y solo enviaremos las tres líneas siguientes:

1
2
3
GET /exemples-web/web01.php HTTP/1.1
Host: localhost
Connection: close
  • línea 1: especifica el documento solicitado y el protocolo HTTP utilizado
  • línea 2: indica el nombre del equipo del script del cliente
  • línea 3: indica que, tras el intercambio, el cliente cerrará la conexión con el servidor

Veamos ahora la respuesta del servidor. Sabemos que ha sido generada por el script PHP [web_01.php]. Arriba vemos los encabezados HTTP de la respuesta. El código del script [web01.php] muestra que no fue él quien los generó. Recordemos la configuración del script del servidor:

Es el servidor web el que ha generado los encabezados HTTP de la respuesta. El script del servidor puede generarlos por sí mismo. Veremos un ejemplo de ello un poco más adelante.

Hemos dicho que la respuesta del servidor web tenía el siguiente formato: encabezados HTTP, línea en blanco, documento. Si el documento es un documento de texto, podemos verlo en la pestaña [Réponse] de Firebug:

 

Esta respuesta ha sido generada por el script [web_01.php].

10.1.2. Un cliente (client1_web_01)

Ahora escribimos un script de cliente para el servicio anterior. Sabemos que el cliente debe:

  • abrir una conexión con el servidor web
  • enviar el texto: encabezados HTTP, línea vacía
  • leer la respuesta completa del servidor hasta que este cierre su conexión con el cliente
  • cerrar la conexión con el servidor

El script de cliente se ejecuta en un entorno de consola de Netbeans:

  • en [1], el script de cliente [client1_web_01.php] se incluye en el proyecto Netbeans [exemples]
  • en [2], las propiedades del proyecto Netbeans [exemples]
  • en [3], el proyecto Netbeans [exemples] se ejecuta en modo «línea de comandos», lo que también hemos denominado modo «consola».

El código del script de cliente es el siguiente:


<?php

// datos
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_01.php";
// apertura de una conexión en el puerto 80 de $HOTE
$connexion = fsockopen($HOTE, $PORT);
// ¿error?
if (!$connexion) {
  print "Erreur : $erreur\n";
  exit;
}
// los encabezados (headers) del protocolo HTTP deben terminar con una línea en blanco
// GET
fputs($connexion, "GET $urlServeur HTTP/1.1\n");
// Host
fputs($connexion, "Host: localhost\n");
// Conexión
fputs($connexion,"Connection: close\n");
// línea en blanco
fputs($connexion,"\n");
// el servidor responderá ahora en el canal $connexion. Enviará todos
// sus datos y luego cerrará el canal. El cliente lee, por tanto, todo lo que llega de $connexion
// hasta que se cierre el canal
while ($ligne = fgets($connexion, 1000)) {
  print "$ligne";
}//mientras
// el cliente cierre a su vez la conexión
fclose($connexion);
// fin
exit;

Comentarios

  • línea 8: apertura de una conexión con el servidor
  • línea 16: comando HTTP GET
  • línea 18: comando HTTP Host
  • línea 20: comando HTTP Conexión
  • línea 22: línea vacía
  • líneas 26-28: lectura de todas las líneas de texto enviadas por el servidor hasta que este cierre la conexión.
  • línea 30: el cliente cierra a su vez la conexión

Resultados

La ejecución del script del cliente produce los siguientes resultados:

1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
Date: Wed, 17 Aug 2011 13:35:00 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Content-Length: 17
Connection: close
Content-Type: text/html

17/08/11 13:35:00

Comentarios

  • líneas 1-7: la respuesta HTTP del servidor web.
  • línea 8: la línea vacía que indica el final de los encabezados HTTP
  • líneas 9 y siguientes: el documento. En este caso, se trata de un texto simple que representa la fecha y la hora actuales. Es el texto escrito por el script PHP en la salida n.º 1.
  • línea 1: el servidor responde que ha encontrado el documento solicitado.
  • línea 2: fecha y hora actuales del servidor
  • línea 3: identidad del servidor web
  • línea 4: indica que el documento que sigue es un documento generado por un script PHP
  • línea 5: número de caracteres del documento
  • línea 6: el servidor indica que, tras enviar el documento, va a cerrar la conexión
  • línea 7: indica que el documento enviado por el servidor es un texto en formato HTML. Esto es incorrecto en este caso. El documento es texto sin formato específico. Cuando el documento no es texto en formato HTML, es el script PHP el que debe indicarlo. Aquí no lo hemos hecho.

10.1.3. Un segundo cliente (client2_web_01)

El cliente anterior mostraba todo lo que le enviaba el servidor web. En la práctica, normalmente se ignoran los encabezados de la respuesta y se utiliza la parte del documento. Aquí intentamos recuperar la fecha y la hora enviadas por el servidor. Vamos a recuperar esta información mediante una expresión regular.


<?php

// recuperar la información enviada por un servidor web
// datos
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_01.php";
// apertura de una conexión en el puerto 80 de $HOTE
$connexion = fsockopen($HOTE, $PORT);
// ¿error?
if (!$connexion) {
  print "Erreur : $erreur\n";
  exit;
}
// los encabezados (headers) del protocolo HTTP deben terminar con una línea en blanco
// GET
fputs($connexion, "GET $urlServeur HTTP/1.1\n");
// Host
fputs($connexion, "Host: localhost\n");
// Conexión
fputs($connexion,"Connection: close\n");
// línea en blanco
fputs($connexion,"\n");
// el servidor responderá ahora en el canal $connexion. Enviará todos
// estos datos y luego cerrará el canal. El cliente lee, por tanto, todo lo que llega de $connexion
// hasta encontrar la línea que busca con el formato dd/mm/aa hh:mm:ss
while ($ligne = fgets($connexion, 1000)) {
  print "$ligne";
  if (preg_match("/(\d\d)\/(\d\d)\/(\d\d) (\d\d):(\d\d):(\d\d)/", $ligne, $champs)) {
     // se recuperan los # campos
    array_shift($champs); // se elimina el primer elemento de la matriz campos
     // se recuperan los 6 campos en 6 variables
    list($j, $m, $a, $h, $i, $s) = $champs;
     // visualización del resultado
    print "\ndateheure=[$j,$m,$a,$h,$i,$s]\n";
  }//if
}//while
// el cliente cierra la conexión a su vez
fclose($connexion);
// fin
exit;

Resultados

HTTP/1.1 200 OK
Date: Wed, 17 Aug 2011 14:14:51 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Content-Length: 17
Connection: close
Content-Type: text/html

17/08/11 14:14:51
dateheure=[17,08,11,14,14,51]

10.2. Recuperación por parte del servidor de los parámetros enviados por el cliente

En el protocolo HTTP, un cliente dispone de dos métodos para pasar parámetros al servidor web:

  • solicita el URL del servicio en la forma

GET url?param1=val1&param2=val2&param3=val3… HTTP/1.0

donde los valores válidos deben codificarse previamente para que ciertos caracteres reservados sean sustituidos por su valor hexadecimal.

  • solicita el URL del servicio en la forma

POST url HTTP/1.0

y, a continuación, entre los encabezados HTTP enviados al servidor, coloca el siguiente encabezado:

Content-length=N

El resto de los encabezados enviados por el cliente terminan con una línea en blanco. A continuación, puede enviar sus datos en el formato

val1&param2=val2&param3=val3…

donde los valores válidos deben, al igual que en el método GET, estar previamente codificados. El número de caracteres enviados al servidor debe ser N, donde N es el valor declarado en el encabezado

Content-length=N

El script PHP, que recupera los parámetros parami anteriores enviados por el cliente, obtiene sus valores de la tabla:

  • $_GET["parami"] para un comando GET
  • $_POST["parami"] para un comando POST

10.2.1. El cliente GET (client1_web_02)

El script PHP que se muestra a continuación envía tres parámetros [nom, prenom, age] al servidor.


<?php

// cliente: envía nombre, apellidos y edad al servidor mediante el método GET
// datos
$HOTE = "localhost";
$PORT = 80;
$URL = "/exemples-web/web_02.php";
list($prenom, $nom, $age) = array("jean-paul", "de la hûche", 45);
// conexión al servidor web
$connexion = fsockopen($HOTE, $PORT);
// retorno en caso de error
if (!$connexion) {
  print "Echec de la connexion au site ($HOTE,$PORT) : $erreur";
  exit;
}//if
// envío de la información al servidor PHP
// se codifica la información
$infos = "prenom=" . urlencode(utf8_decode($prenom)) . "&nom=" . urlencode(utf8_decode($nom)) . "&age=" . urlencode("$age");
// seguimiento de la consola
print "infos envoyées au serveur (GET)=$infos\n";
print "URL demandée=[$URL?$infos]\n\n";
// los encabezados (headers) del protocolo HTTP deben terminar con una línea en blanco
// GET
fputs($connexion, "GET $URL?$infos HTTP/1.1\n");
// Host
fputs($connexion, "Host: localhost\n");
// Conexión
fputs($connexion,"Connection: close\n");
// línea en blanco
fputs($connexion,"\n");
// el servidor responderá ahora en el canal $connexion. Enviará todos
// sus datos y luego cerrará el canal. El cliente lee todo lo que llega desde $connexion hasta que se cierre el canal
while ($ligne = fgets($connexion, 1000))
  print "$ligne";
// el cliente cierra la conexión a su vez
fclose($connexion);

Comentarios

  • línea 7: Url del script del servidor
  • línea 8: los valores de los 3 parámetros
  • línea 10: apertura de una conexión con el servidor web
  • línea 18: codificación de los 3 parámetros. Nos encontramos en un script escrito en Netbeans con una codificación de caracteres en UTF-8. Por lo tanto, los 3 valores de los parámetros de la línea 8 se codifican en UTF-8. La función utf8_decode convierte su codificación a ISO-8859-1. Una vez hecho esto, pueden codificarse para URL. Todos los caracteres no alfabéticos se sustituyen por %xx, donde xx es el valor hexadecimal del carácter. Los espacios se sustituyen por el signo +.
  • línea 24: el URL solicitado es $URL?$infos, donde $infos tiene la forma nombre=val1&apellido=val2&edad=val3.

10.2.2. El servidor (web_02)

El servidor se limita a mostrar lo que recibe.


<?php

// gestión de errores
ini_set("display_errors", "off");

// recuperación por parte del servidor de la información enviada por el cliente
// aquí nombre=P&apellido=N&edad=A
// esta información está disponible automáticamente en las variables
// $_GET['prenom'], $_GET['nom'], $_GET['age']
// se envían al cliente

// encabezado UTF-8
header("Content-Type: text/plain; charset=utf-8");

// parámetros enviados al servidor
$prenom = isset($_GET['prenom']) ? $_GET['prenom'] : "";
$nom = isset($_GET['nom']) ? $_GET['nom'] : "";
$age = isset($_GET['age']) ? $_GET['age'] : "";

// respuesta al cliente
$réponse = "informations reçues du client [" .
        utf8_encode(htmlspecialchars($prenom, ENT_QUOTES)) .
        "," . utf8_encode(htmlspecialchars($nom, ENT_QUOTES)) .
        "," . utf8_encode(htmlspecialchars($age, ENT_QUOTES)) . "]\n";
print $réponse;

Comentarios

  • línea 13: establece el encabezado HTTP «Content-Type». Por defecto, el servidor web envía el encabezado
Content-Type: text/html

que indica que la respuesta es texto en formato HTML. En este caso, la respuesta será texto sin formato con caracteres codificados en UTF-8:

Content-Type: text/plain; charset=utf-8

Los encabezados HTTP deben enviarse antes de la respuesta del servidor. Por lo tanto, en el código anterior, la llamada a la función header se encontrará antes de cualquier instrucción print.

  • Líneas 16-18: se recuperan los 3 parámetros en la matriz $_GET.
  • línea 21: se construye la cadena de caracteres que se enviará como respuesta al cliente. Algunos caracteres tienen significados especiales en HTML y deben sustituirse por entidades HTML para que se muestren. htmlspecialchars($string) sustituye todos estos caracteres por su equivalente en la cadena $string. Por ejemplo, el carácter $ se convierte en &amp. A continuación, como hemos indicado en la línea 13 que la respuesta sería el texto UTF-8, codificamos en UTF-8 los valores recuperados.
  • línea 25: la respuesta se envía al cliente

Prueba 1

Ejecutemos el script [web_02] a partir de Netbeans. A continuación, se abre un navegador para mostrar el URL [http://localhost/exemples-web/web_02.php]:

  • en [1], el navegador muestra el URL [http://localhost/exemples-web/web_02.php]. Como no hemos añadido parámetros a este Url, el servidor ha respondido con parámetros vacíos. Recordemos que la respuesta del servidor es la que se escribe con la instrucción print.
  • En [2], añadimos parámetros al final de URL. Esta vez, el script del servidor los devuelve correctamente.

Cabe señalar que Netbeans no es necesario para ejecutar un script de servidor. Basta con escribir URL del script de servidor en un navegador para que este se ejecute.

Prueba 2

Se ejecuta en Netbeans el cliente [client1_web_02.php]. Se recibe la siguiente respuesta:

infos envoyées au serveur (GET)=prenom=jean-paul&nom=de+la+h%FBche&age=45
URL demandée=[/exemples-web/web_02.php?prenom=jean-paul&nom=de+la+h%FBche&age=45]

HTTP/1.1 200 OK
Date: Wed, 17 Aug 2011 14:31:01 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Content-Length: 59
Connection: close
Content-Type: text/plain; charset=utf-8

informations reçues du client [jean-paul,de la hûche,45]
  • línea 1: codificación de los 3 parámetros. Se observa que el carácter û se ha convertido en %FB.
  • línea 12: la respuesta del servidor

10.2.3. El cliente POST (client2_web_03)

Un cliente Http envía al servidor web la siguiente secuencia de texto: encabezados HTTP, línea vacía, documento. En el cliente anterior, esta secuencia era la siguiente:

GET /url?paramètres HTTP/1.1
… autres entêtes HTTP
ligne vide

No había documento. Existe otra forma de transmitir parámetros, el método denominado POST. En este caso, la secuencia de texto enviada al servidor web es la siguiente:

POST /url HTTP/1.1
… autres entêtes HTTP
ligne vide
paramètres

En esta ocasión, los parámetros que en el cliente GET se incluían en los encabezados HTTP, forman parte, en el cliente POST, del documento enviado tras los encabezados.

El script del cliente POST es el siguiente:


<?php

// cliente: envía nombre, apellidos y edad al servidor mediante el método POST
// datos
$HOTE = "localhost";
$PORT = 80;
$URL = "/exemples-web/web_03.php";
list($prenom, $nom, $age) = array("jean-paul", "de la hûche", 45);
// conexión al servidor web
$connexion = fsockopen($HOTE, $PORT);
// retorno en caso de error
if (!$connexion) {
  print "Echec de la connexion au site ($HOTE,$PORT) : $erreur";
  exit;
}//if
// envío de la información al servidor PHP
// se codifica la información
$infos = "prenom=" . urlencode(utf8_decode($prenom)) . "&nom=" . urlencode(utf8_decode($nom)) . "&age=" . urlencode("$age");
print "client : infos envoyées au serveur (POST) : $infos\n";
// se conecta al $URL enviándole (POST) los parámetros
// los encabezados (headers) del protocolo HTTP deben terminar con una línea en blanco
// POST
fputs($connexion, "POST $URL HTTP/1.1\n");
// Host
fputs($connexion, "Host: localhost\n");
// Conexión
fputs($connexion,"Connection: close\n");
// Content-type
fputs($connexion, "Content-type: application/x-www-form-urlencoded\n");
// Longitud del contenido
// se envía el tamaño (número de caracteres) de la información que se va a enviar
fputs($connexion, "Content-length: " . strlen($infos) . "\n");
// se envía una línea en blanco
fputs($connexion, "\n");
// se envía la información
fputs($connexion, $infos);
// el servidor responderá ahora en el canal $connexion. Enviará todos
// sus datos y luego cerrará el canal. El cliente lee todo lo que llega de $connexion
// hasta que se cierre el canal
while ($ligne = fgets($connexion, 1000))
  print "$ligne";
// el cliente cierra la conexión a su vez
fclose($connexion);

Comentarios

  • línea 7: el URL del servicio web al que se conectará el cliente POST. Este servicio web se describirá próximamente.
  • línea 8: los parámetros que se deben transmitir al servicio web
  • línea 10: conexión al servidor web
  • línea 18: codificación de los parámetros que se enviarán al servicio web
  • línea 23: comando Http POST
  • línea 25: comando Http Host
  • línea 27: comando Http Connection
  • línea 29: comando Http Content-type. Ya nos hemos encontrado con este encabezado HTTP. Está presente cada vez que se envía un documento. Un servidor web que envía un documento Html utiliza el encabezado HTTP
Content-type : text/html

Si envía texto sin formato, utiliza el encabezado HTTP

Content-type : text/plain

Nuestro cliente POST envía un documento que es un texto con el formato param1=val1&param2=val2&.... Este tipo de documento tiene el tipo application/x-www-form-urlencoded. No explicaremos por qué, ya que eso nos obligaría a explicar qué es un formulario web.

  • Línea 32: comando Content-length. Ya nos hemos encontrado con este encabezado HTTP. Está presente cada vez que se envía un documento. Indica el número de bytes del documento.
  • línea 34: la línea vacía que indica el final de los encabezados HTTP
  • línea 36: envío de los parámetros
  • líneas 40-41: lectura de la respuesta completa del servidor
  • línea 43: cierre de la conexión

10.2.4. El servidor (web_03)

El servicio web [web_03] hace lo mismo que el servicio web [web_02]. Lee los parámetros enviados por el cliente POST y los reenvía al cliente. Su código es el siguiente:


<?php

// gestión de errores
ini_set("display_errors", "off");
// encabezado UTF-8
header("Content-Type: text/plain; charset=utf-8");

// recuperación por parte del servidor de la información enviada por el cliente
// aquí nombre=P&apellidos=N&edad=A
// esta información está disponible automáticamente en las variables
// $_POST['prenom'], $_POST['nom'], $_POST['age']
// se devuelven al cliente
// parámetros enviados al servidor
$prenom = isset($_POST['prenom']) ? $_POST['prenom'] : "";
$nom = isset($_POST['nom']) ? $_POST['nom'] : "";
$age = isset($_POST['age']) ? $_POST['age'] : "";
// respuesta al cliente
$réponse = "informations reçues du client [" .
        utf8_encode(htmlspecialchars($prenom, ENT_QUOTES)) .
        "," . utf8_encode(htmlspecialchars($nom, ENT_QUOTES)) .
        "," . utf8_encode(htmlspecialchars($age, ENT_QUOTES)) . "]\n";
print $réponse;

Comentarios

  • líneas 14-16: los parámetros enviados por un cliente POST quedan disponibles en la matriz $_POST para el servicio web que los recibe.
  • Línea 6: encabezado HTTP Content-Type. Puede sorprender que en los encabezados HTTP no se encuentre el encabezado HTTP Content-Length, que indica el tamaño del documento devuelto al cliente. Hemos visto que el servidor web enviaba encabezados HTTP por defecto. El encabezado Content-Length forma parte de ellos.

Resultados

Una vez escrito el script del servidor en NetBeans, queda inmediatamente disponible a través del servidor Apache de WampServer. Recordemos que esto se consigue mediante la configuración (véase el apartado 10). Iniciamos el cliente que consulta al servidor y recibimos entonces la siguiente respuesta:

client : infos envoyées au serveur (POST) : prenom=jean-paul&nom=de+la+h%FBche&age=45
HTTP/1.1 200 OK
Date: Sat, 20 Aug 2011 12:55:19 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Content-Length: 59
Connection: close
Content-Type: text/plain; charset=utf-8

informations reçues du client [jean-paul,de la hûche,45]
  • líneas 2-10: la respuesta del servidor
  • líneas 2-8: los encabezados Http
  • línea 10: el documento
  • línea 6: el encabezado HTTP Content-Length. Como no es el script del servidor el que ha generado este encabezado, significa que lo ha generado el servidor web.
  • línea 8: el único encabezado generado por el script del servidor

10.3. Recuperación de las variables de entorno del servidor WEB

Un script de servidor se ejecuta en un entorno web que puede conocer. Este entorno se almacena en el diccionario $_SERVER. En primer lugar, escribimos una aplicación de servidor que envía a sus clients el contenido de este diccionario.

10.3.1. El servidor (web_04)


<?php

// gestión de errores
ini_set("display_errors", "off");
// encabezado UTF-8
header("Content-Type: text/plain; charset=utf-8");

// se devuelve al cliente la lista de variables disponibles en el entorno del servidor
foreach ($_SERVER as $clé => $valeur) {
  print "[$clé,$valeur]\n";
}
  • los pares (clave, valor) del diccionario $_SERVER se envían a clients.

El resultado obtenido cuando el cliente es un navegador web es el siguiente:

Image

Este es el significado de algunas de las variables (para Windows. En Linux, serían diferentes):

HTTP_CMDE
CMDE representa el encabezado HTTP enviado por el cliente. Se tiene acceso a todos estos encabezados.
PATH
la ruta de los ejecutables en la máquina en la que se ejecuta el script del servidor
COMSPEC
la ruta del intérprete de comandos DOS
PATHEXT
las extensiones de los archivos ejecutables
WINDIR
la carpeta de instalación de Windows
SERVER_SIGNATURE
la firma del servidor web. Aquí no hay nada.
SERVER_SOFTWARE
el tipo de servidor web
SERVER_NAME 
El nombre de Internet del equipo del servidor web
SERVER_PORT
el puerto de escucha del servidor web
SERVER_ADDR
la dirección IP del servidor web
REMOTE_ADDR
la dirección IP del cliente. En este caso, el cliente se encontraba en la misma máquina que el servidor.
REMOTE_PORT
el puerto de comunicación del cliente
DOCUMENT_ROOT
la raíz del árbol de documentos servidos por el servidor web
SERVER_ADMIN
la dirección de correo electrónico del administrador del servidor web
SCRIPT_FILENAME
la ruta completa del script del servidor
SERVER_PROTOCOL
el protocolo version utilizado por el servidor web
REQUEST_METHOD
la orden Http utilizada por el cliente. Hay cuatro: GET, POST, PUT, DELETE
QUERY_STRING
¿Los parámetros enviados con una orden GET /url?parámetros
REQUEST_URI
el URL solicitado por el cliente. Si el navegador solicita el URL http://máquina[:port]/uri, obtendremos REQUEST_URI=uri
SCRIPT_NAME
$_SERVER['SCRIPT_FILENAME']=$_SERVER['DOCUMENT_ROOT'].$_SERVER['SCRIPT_NAME']

10.3.2. El cliente (client1_web_04)

El cliente se limita a mostrar todo lo que le envía el servidor.


<?php

// datos
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_04.php";
// apertura de una conexión en el puerto 80 de $HOTE
$connexion = fsockopen($HOTE, $PORT);
// ¿error?
if (!$connexion) {
  print "Erreur : $erreur\n";
  exit;
}

// se conecta a un URL al servidor web
// los encabezados (headers) del protocolo HTTP deben terminar con una línea en blanco
// GET
fputs($connexion, "GET $urlServeur HTTP/1.1\n");
// Host
fputs($connexion, "Host: localhost\n");
// Conexión
fputs($connexion,"Connection: close\n");
// línea en blanco
fputs($connexion,"\n");
// el servidor responderá ahora en el canal $connexion. Enviará todos
// sus datos y luego cerrará el canal. Por lo tanto, el cliente lee todo lo que llega de $connexion
// hasta que se cierre el canal
while ($ligne = fgets($connexion, 1000)) {
  print "$ligne";
}//mientras
// el cliente cierre a su vez la conexión
fclose($connexion);
// fin
exit;

Resultados

HTTP/1.1 200 OK
Date: Sat, 20 Aug 2011 14:11:58 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Content-Length: 1353
Connection: close
Content-Type: text/plain; charset=utf-8

[HTTP_HOST,localhost]
[HTTP_CONNECTION,close]
[PATH,C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\Microsoft SQL Server\90\Tools\binn\;...;]
[SystemRoot,C:\Windows]
[COMSPEC,C:\Windows\system32\cmd.exe]
[PATHEXT,.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC]
[WINDIR,C:\Windows]
[SERVER_SIGNATURE,]
[SERVER_SOFTWARE,Apache/2.2.17 (Win32) PHP/5.3.5]
[SERVER_NAME,localhost]
[SERVER_ADDR,127.0.0.1]
[SERVER_PORT,80]
[REMOTE_ADDR,127.0.0.1]
[DOCUMENT_ROOT,C:/serveursSGBD/wamp21/www/]
[SERVER_ADMIN,admin@localhost]
[SCRIPT_FILENAME,C:/serveursSGBD/wamp21/www/exemples-web/web_04.php]
[REMOTE_PORT,54552]
[GATEWAY_INTERFACE,CGI/1.1]
[SERVER_PROTOCOL,HTTP/1.1]
[REQUEST_METHOD,GET]
[QUERY_STRING,]
[REQUEST_URI,/exemples-web/web_04.php]
[SCRIPT_NAME,/exemples-web/web_04.php]
[PHP_SELF,/exemples-web/web_04.php]
[REQUEST_TIME,1313849518]

10.4. Gestión de sesiones WEB

En los ejemplos de cliente/servidor anteriores, el funcionamiento era el siguiente:

  • el cliente abre una conexión al puerto 80 del servidor web
  • envía la secuencia de texto: encabezados Http, línea en blanco, [document]
  • en respuesta, el servidor envía una secuencia del mismo tipo
  • el servidor cierra la conexión con el cliente
  • el cliente cierra la conexión con el servidor

Si el mismo cliente realiza poco después una nueva solicitud al servidor web, se crea una nueva conexión entre el cliente y el servidor. Este no puede saber si el cliente que se conecta ya ha estado antes o si se trata de una primera solicitud. Entre dos conexiones, el servidor «olvida» a su cliente. Por esta razón, se dice que el protocolo HTTP es un protocolo sin estado. Sin embargo, resulta útil que el servidor recuerde a sus clientes. Así, si una aplicación es segura, el cliente enviará al servidor un nombre de usuario y una contraseña para identificarse. Si el servidor «olvida» a su cliente entre dos conexiones, este deberá identificarse en cada nueva conexión, lo cual no es viable.

Para realizar el seguimiento de un cliente, el servidor procede de la siguiente manera: cuando recibe una primera solicitud de un cliente, incluye en su respuesta un identificador que el cliente debe reenviarle en cada nueva solicitud. Gracias a este identificador, diferente para cada cliente, el servidor puede reconocer a un cliente. A continuación, puede gestionar una memoria para ese cliente en forma de un archivo asociado de manera única al identificador del cliente.

Técnicamente, esto es lo que ocurre:

  • en la respuesta a un nuevo cliente, el servidor incluye el encabezado HTTP Set-Cookie: MotClé=Identificador. Solo lo hace en la primera solicitud.
  • En sus solicitudes posteriores, el cliente reenviará su identificador a través del encabezado HTTP Cookie: MotClé=Identificador para que el servidor lo reconozca.

Cabe preguntarse cómo sabe el servidor que se trata de un cliente nuevo en lugar de uno que ya ha visitado la página. Es la presencia del encabezado HTTP Cookie en los encabezados Http del cliente lo que se lo indica. En el caso de un cliente nuevo, este encabezado no está presente.

El conjunto de conexiones de un cliente determinado se denomina sesión.

10.4.1. El archivo de configuración

Para que la gestión de sesiones funcione correctamente con PHP, hay que comprobar que esté correctamente configurado. En Windows, su archivo de configuración es PHP.ini. Según el contexto de ejecución (consola, web), el archivo de configuración [PHP.ini] debe buscarse en carpetas diferentes. Para localizarlas, se utilizará el siguiente script:

1
2
3
4
<?php

// información PHP
phpinfo();

En la línea 4, la función phpinfo proporciona información sobre el intérprete PHP que ejecuta el script. En concreto, indica la ruta del archivo de configuración [PHP.ini] utilizado.

En un entorno de consola, se obtiene un resultado similar al siguiente:

1
2
3
4
5
6
Configuration File (PHP.ini) Path => C:\Windows
Loaded Configuration File => C:\serveursSGBD\wamp21\bin\PHP\php5.3.5\PHP.ini
Scan this dir for additional .ini files => (none)
Additional .ini files parsed => (none)

línea 2: el archivo de configuración principal es c:\windows\PHP.ini

línea 3: un archivo de configuración secundario es C:\serveursSGBD\wamp21\bin\PHP\php5.3.5\PHP.ini. Permite modificar ciertas opciones de configuración del archivo de configuración principal.

En un entorno web, se obtiene el siguiente resultado:

Image

El archivo de configuración secundario aquí no es el mismo que en el entorno de consola. Es este último el que vamos a consultar. En este archivo encontramos una sección de sesión:

[Session]
session.save_handler = files
session.save_path = "C:/serveursSGBD/wamp21/tmp"
session.use_cookies = 1
session.use_only_cookies = 1
session.name = PHPSESSID
session.auto_start = 0
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_domain =
session.serialize_handler = PHP
session.cache_expire = 180
session.hash_function = 0
  • línea 1: los datos de una sesión de cliente se guardan en un archivo
  • línea 3: la carpeta de almacenamiento de los datos de sesión. Si esta carpeta no existe, no se señala ningún error y la gestión de sesiones no funciona.
  • líneas 4-5: indican que el identificador de sesión se gestiona mediante los encabezados Http Set-Cookie y Cookie
  • línea 6: el encabezado Set-Cookie tendrá el formato Set-Cookie: PHPSESSID=identifiant_de_session
  • línea 7: una sesión de cliente no se inicia automáticamente. El script del servidor debe solicitarla explícitamente mediante una instrucción session_start().

10.4.2. El servidor 1 (web_05)

La gestión del identificador de sesión es transparente para un servicio web. Este identificador es gestionado por el servidor web. Un servicio web tiene acceso a la sesión del cliente mediante la instrucción session_start(). A partir de ese momento, el servicio web puede leer/escribir datos en la sesión del cliente a través del diccionario $_SESSION. El siguiente código muestra la gestión en sesión de tres contadores.


<?php

// gestión de errores
ini_set("display_errors", "off");
// encabezado UTF-8
header("Content-Type: text/plain; charset=utf-8");

// se abre una sesión
session_start();
// se introducen 3 variables en la sesión
if (!isset($_SESSION['N1'])) {
  $_SESSION['N1'] = 0;
}
if (!isset($_SESSION['N2'])) {
  $_SESSION['N2'] = 10;
}
if (!isset($_SESSION['N3'])) {
  $_SESSION['N3'] = 100;
}
// incremento de las 3 variables
$_SESSION['N1']++;
$_SESSION['N2']++;
$_SESSION['N3']++;
// envío de información al cliente
print "N1=".$_SESSION['N1']."\n";
print "N2=".$_SESSION['N2']."\n";
print "N3=".$_SESSION['N3']."\n";
// fin de la sesión
session_close();
  • línea 9: inicio de una sesión de cliente
  • líneas 11-13: la tabla $_SESSION es un diccionario de pares (clave, valor). Los datos introducidos en este diccionario persisten a lo largo de las solicitudes de un mismo cliente. Es la memoria del cliente en el servidor.
  • líneas 11-19: si los tres contadores N1, N2, N3 no están en sesión, se añaden a ella.
  • líneas 21-23: se incrementan en una unidad
  • líneas 25-27: se envía su valor al cliente

En la relación cliente/servidor, la gestión de la sesión del cliente en el servidor depende de ambos actores, el cliente y el servidor:

  • el servidor se encarga de enviar un identificador a su cliente en su primera solicitud
  • el cliente se encarga de reenviar este identificador en cada nueva solicitud. Si no lo hace, el servidor considerará que se trata de un nuevo cliente y generará un nuevo identificador para una nueva sesión.

Resultados

Utilizamos como cliente un navegador web. Por defecto (en realidad, por configuración), este devuelve al servidor los identificadores de sesión que este le envía. A medida que se realizan las solicitudes, el navegador recibirá los tres contadores enviados por el servidor y verá cómo se incrementan sus valores.

  • en [1], la primera solicitud al servicio web [web_05]
  • a [2]; la tercera solicitud muestra que los contadores se han incrementado correctamente. Se comprueba que los valores de los contadores se memorizan a lo largo de las solicitudes.

Utilicemos Firebug para ver los encabezados Http intercambiados entre el servidor y el cliente. Cerramos Firefox para finalizar la sesión actual con el servidor, lo volvemos a abrir y activamos Firebug. Solicitamos el servicio |web_05]:

Arriba vemos el identificador de sesión enviado por el servidor en su respuesta a la primera solicitud del cliente. Utiliza el encabezado HTTP Set-Cookie.

Realicemos una nueva solicitud actualizando (F5) la página en el navegador web:

En lo anterior se observan dos cosas:

  • el navegador web devuelve el identificador de sesión con el encabezado HTTP Cookie.
  • En su respuesta, el servicio web ya no incluye este identificador. Ahora es el cliente el encargado de enviarlo en cada una de sus solicitudes.

10.4.3. El cliente 1 (client1_web_05)

Ahora escribimos un script de cliente a partir del script de servidor anterior. En su gestión de la sesión, debe comportarse como el navegador web:

  • en la respuesta del servidor a su primera solicitud, debe encontrar el identificador de sesión que el servidor le envía. Sabe que lo encontrará en el encabezado HTTP Set-Cookie.
  • En cada una de sus solicitudes posteriores, debe reenviar al servidor el identificador que ha recibido. Lo hará con el encabezado HTTP Cookie.

El código del cliente es el siguiente:


<?php

// datos
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_05.php";
// pruebas
$cookie = "";
for ($i = 0; $i < 5; $i++) {
  list($erreur, $cookie, $N1, $N2, $N3) = connecte($HOTE, $PORT, $urlServeur, $cookie);
  print "----------------------------\n";
  print "client(erreur,cookie,N1,N2,N3)=[$erreur,$cookie,$N1,$N2,$N3]\n";
  print "----------------------------\n";
}
// fin
exit;

function connecte($HOTE, $PORT, $urlServeur, $cookie) {
  // conecta al cliente a ($HOTE,$PORT,$urlServeur)
  // envía la cookie $cookie si esta no está vacía
  // muestra todas las líneas recibidas en respuesta
  // apertura de una conexión en el puerto 80 de $HOTE
  $connexion = fsockopen($HOTE, $PORT);
  // ¿error?
  if (!$connexion)
    return array("erreur lors de la connexion au serveur ($HOTE, $PORT)");
  // se conecta a $urlserveur
  // los encabezados (headers) del protocolo HTTP deben terminar con una línea en blanco
  // GET
  fputs($connexion, "GET $urlServeur HTTP/1.1\n");
  // Host
  fputs($connexion, "Host: localhost\n");
  // Conexión
  fputs($connexion, "Connection: close\n");
  // se envía la cookie si no está vacía
  if ($cookie) {
    fputs($connexion, "Cookie: $cookie\n");
  }//if
  // envío de línea vacía
  fputs($connexion, "\n");
  // se muestra la respuesta del servidor web
  // y nos aseguramos de recuperar la posible cookie y los valores de Ni
  $N = "";
  while ($ligne = fgets($connexion, 1000)) {
    print "$ligne";
    // cookie: solo en la primera respuesta
    if (!$cookie) {
      if (preg_match("/^Set-Cookie: (.*?)\s*$/", $ligne, $champs)) {
        $cookie = $champs[1];
      }
    }
    // valor de N1
    if (preg_match("/^N1=(.*?)\s*$/", $ligne, $champs))
      $N1 = $champs[1];
    // valor de N2
    if (preg_match("/^N2=(.*?)\s*$/", $ligne, $champs))
      $N2 = $champs[1];
    // valor de N3
    if (preg_match("/^N3=(.*?)\s*$/", $ligne, $champs))
      $N3 = $champs[1];
  }//mientras
  // se cierra la conexión
  fclose($connexion);
  // retorno
  return array("", $cookie, $N1, $N2, $N3);
}

Comentarios

  • líneas 3-16: el programa principal
  • líneas 18-67: la función de conexión
  • líneas 9-14: el cliente llama 5 veces al servidor y muestra los valores sucesivos de los contadores N1, N2 y N3. Si la sesión se gestiona correctamente, estos contadores deberían incrementarse en 1 con cada nueva solicitud.
  • Línea 10: la función «connecte» utiliza los parámetros $HOTE, $PORT y $urlServeur para conectar al cliente con el servicio web. El parámetro $cookie representa el identificador de sesión. En la primera llamada, es una cadena vacía. En las siguientes, es el identificador de sesión enviado por el servidor en respuesta a la primera llamada del cliente. La función «connecte» devuelve como resultados los valores de los tres contadores $N1, $N2, $N3, el identificador de sesión $cookie y un posible error $erreur.
  • línea 18: la función se conecta con las características de un cliente Http clásico. Solo comentamos las novedades.
  • líneas 30-40: envío de los encabezados Http.
  • líneas 36-38: si se conoce el identificador de sesión, se envía al servidor
  • líneas 44-66: procesamiento de todas las líneas de texto enviadas por el servidor
  • líneas 47-51: si aún no se ha recuperado el identificador de sesión, se recupera en el encabezado HTTP Set-Cookie mediante una expresión regular.
  • líneas 53-54: el contador N1 también se obtiene mediante una expresión regular
  • líneas 56-57, 59-60: lo mismo ocurre con los contadores N2 y N3
  • línea 63: cierre de la conexión con el servidor.
  • línea 65: devolución de los resultados en forma de tabla.

Resultados

La ejecución del script de cliente provoca la siguiente visualización en la consola Netbeans:

HTTP/1.1 200 OK
Date: Sun, 21 Aug 2011 13:59:17 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Set-Cookie: PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 18
Connection: close
Content-Type: text/plain; charset=utf-8

N1=1
N2=11
N3=101
----------------------------
client(erreur,cookie,N1,N2,N3)=[,PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/,1,11,101]
----------------------------
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2011 13:59:18 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 18
Connection: close
Content-Type: text/plain; charset=utf-8

N1=2
N2=12
N3=102
----------------------------
client(erreur,cookie,N1,N2,N3)=[,PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/,2,12,102]
----------------------------
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2011 13:59:18 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 18
Connection: close
Content-Type: text/plain; charset=utf-8

N1=3
N2=13
N3=103
----------------------------
client(erreur,cookie,N1,N2,N3)=[,PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/,3,13,103]
----------------------------
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2011 13:59:18 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 18
Connection: close
Content-Type: text/plain; charset=utf-8

N1=4
N2=14
N3=104
----------------------------
client(erreur,cookie,N1,N2,N3)=[,PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/,4,14,104]
----------------------------
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2011 13:59:18 GMT
Server: Apache/2.2.17 (Win32) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 18
Connection: close
Content-Type: text/plain; charset=utf-8

N1=5
N2=15
N3=105
----------------------------
client(erreur,cookie,N1,N2,N3)=[,PHPSESSID=ohiqtkv7hu2b26kdshjtqms9p7; path=/,5,15,105]
  • línea 5: en su primera respuesta, el servidor envía el identificador de sesión. En las respuestas siguientes, ya no lo envía.
  • Se ve claramente que el servidor web conserva los valores de (N1, N2, N3) a lo largo de las solicitudes del cliente. Esto es lo que se denomina seguimiento de sesión.

Los dos ejemplos siguientes muestran que también se pueden guardar los valores de una matriz o de un objeto.

10.4.4. El servidor 2 (web_06)

El siguiente script de servidor muestra que se puede guardar en la sesión una matriz o un diccionario.


<?php

// gestión de errores
ini_set("display_errors", "off");
// encabezado UTF-8
header("Content-Type: text/plain; charset=utf-8");

// se inicia sesión
session_start();
// se guardan una matriz y un diccionario
// se inicializa o modifica la matriz
if (isset($_SESSION['tableau'])) {
  for ($i = 0; $i < count($_SESSION['tableau']); $i++) {
    $_SESSION['tableau'][$i]++;
  }
} else {
  for ($i = 0; $i < 10; $i++) {
    $_SESSION['tableau'][$i] = $i * 10;
  }
}
// se inicializa o modifica el diccionario
if (isset($_SESSION['dico'])) {
  foreach (array_keys($_SESSION['dico']) as $clé) {
    $_SESSION['dico'][$clé]++;
  }
} else {
  $_SESSION['dico'] = array("zéro" => 0, "dix" => 10, "vingt" => 20);
}
// envío de información al cliente
print "tableau=" . join(",", $_SESSION['tableau']) . "\n";
print "dico=";
foreach ($_SESSION['dico'] as $clé => $valeur) {
  print "($clé,$valeur) ";
}
print "\n";

Comentarios

  • líneas 17-19: un array se introduce inicialmente en la sesión si aún no está allí
  • líneas 12-15: si ya está en la sesión, sus elementos se incrementan en 1
  • línea 27: se introduce en la sesión un diccionario con valores numéricos si aún no está ahí
  • líneas 22-25: si ya está presente, sus valores numéricos se incrementan en 1
  • líneas 30-35: la matriz y el diccionario se envían al cliente

10.4.5. El cliente 2 (client1_web_06)


<?php

// datos
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_06.php";
// pruebas
$cookie = "";
for ($i = 0; $i < 5; $i++) {
  connecte($HOTE, $PORT, $urlServeur, $cookie);
}
// fin
exit;

function connecte($HOTE, $PORT, $urlServeur, &$cookie) {
  // conecta al cliente a ($HOTE,$PORT,$urlServeur)
  // envía la cookie $cookie si esta no está vacía
  // muestra todas las líneas recibidas en respuesta
  // la cookie se pasa por referencia para ser compartida entre
  // el programa llamado y el programa llamante
  // apertura de una conexión en el puerto $PORT de $HOTE
  $connexion = fsockopen($HOTE, $PORT);
  // ¿error?
  if (!$connexion)
    return array("erreur lors de la connexion au serveur ($HOTE, $PORT)");
  // los encabezados (headers) del protocolo HTTP deben terminar con una línea en blanco
  // GET
  fputs($connexion, "GET $urlServeur HTTP/1.1\n");
  // Host
  fputs($connexion, "Host: localhost\n");
  // Conexión
  fputs($connexion, "Connection: close\n");
  // se envía la cookie si no está vacía
  if ($cookie) {
    fputs($connexion, "Cookie: $cookie\n");
  }
  // envío de línea vacía
  fputs($connexion, "\n");
  // se muestra la respuesta del servidor web
  // y nos aseguramos de recuperar la posible cookie
  while ($ligne = fgets($connexion, 1000)) {
    print "$ligne";
    // cookie: solo en la primera respuesta
    if (!$cookie) {
      if (preg_match("/^Set-Cookie: (.*?)\s*$/", $ligne, $champs)) {
        $cookie = $champs[1];
      }
    }
  }
  // se cierra la conexión
  fclose($connexion);
  // volver
  return "";
}

El código del cliente es similar al código del cliente ya comentado.

Resultados

HTTP/1.1 200 OK
...
Set-Cookie: PHPSESSID=6lvttr0uhpj5q3sl91h4h7p322; path=/
...

tableau=0,10,20,30,40,50,60,70,80,90
dico=(zéro,0) (dix,10) (vingt,20) 

HTTP/1.1 200 OK
...

tableau=1,11,21,31,41,51,61,71,81,91
dico=(zéro,1) (dix,11) (vingt,21) 

HTTP/1.1 200 OK
...

tableau=2,12,22,32,42,52,62,72,82,92
dico=(zéro,2) (dix,12) (vingt,22) 

HTTP/1.1 200 OK
...

tableau=3,13,23,33,43,53,63,73,83,93
dico=(zéro,3) (dix,13) (vingt,23) 

HTTP/1.1 200 OK
...

tableau=4,14,24,34,44,54,64,74,84,94
dico=(zéro,4) (dix,14) (vingt,24) 

10.4.6. El servidor 3 (web_07)

El siguiente script de servidor muestra que se puede poner un objeto en sesión.


<?php

// gestión de errores
ini_set("display_errors", "off");
// encabezado UTF-8
header("Content-Type: text/plain; charset=utf-8");

// se inicia sesión
session_start();
// se inicializa o modifica un objeto Persona
if (isset($_SESSION['personne'])) {
  $personne = $_SESSION['personne'];
  // se incrementa la edad
  $personne->setAge($personne->getAge() + 1);
} else {
  // se define la persona
  $_SESSION['personne'] = new Personne("paul", "langévin", 10);
}
// visualización al cliente
print "personne=".$_SESSION['personne']."\n";
// fin
exit;

// ----------------------------------------------------------------
class Personne {

// atributos de la clase
  private $prénom;
  private $nom;
  private $âge;

// getters y setters
  public function getPrénom() {
    return $this->prénom;
  }

  public function getNom() {
    return $this->nom;
  }

  public function getAge() {
    return $this->âge;
  }

  public function setPrénom($prénom) {
    $this->prénom = $prénom;
  }

  public function setNom($nom) {
    $this->nom = $nom;
  }

  public function setAge($age) {
    $this->âge = $age;
  }

// constructor
  function __construct($prénom, $nom, $âge) {
    // se pasa por los set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

// método toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }

}

Comentarios

  • línea 17: se pone en sesión un objeto de tipo Persona si aún no está ahí.
  • líneas 11-15: si ya está, se incrementa su edad en 1
  • línea 20: se envía el objeto de tipo Persona al cliente.

10.4.7. El cliente 3 (client1_web_07)


<?php

// datos
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_07.php";
// pruebas
$cookie = "";
for ($i = 0; $i < 5; $i++) {
  connecte($HOTE, $PORT, $urlServeur, $cookie);
}//if
// fin
exit;

function connecte($HOTE, $PORT, $urlServeur, &$cookie) {
...
}

Comentarios

  • línea 15: la función «conectar» es idéntica a la del script de cliente anterior

Resultados

HTTP/1.1 200 OK
...

personne=[paul,langévin,10]

HTTP/1.1 200 OK
...

personne=[paul,langévin,11]

HTTP/1.1 200 OK
...

personne=[paul,langévin,12]

HTTP/1.1 200 OK
...

personne=[paul,langévin,13]

HTTP/1.1 200 OK
...

personne=[paul,langévin,14]