Skip to content

10. Servidores PHP

Uma vez que os programas PHP podem ser executados por um servidor web, tal programa torna-se um programa do lado do servidor capaz de servir múltiplos clientes. Do ponto de vista do cliente, chamar um serviço web é equivalente a solicitar o URL desse serviço. O cliente pode ser escrito em qualquer linguagem, incluindo PHP. Neste último caso, utilizamos as funções de rede que acabámos de discutir. Também precisamos de saber como «comunicar» com um serviço web, ou seja, compreender o protocolo HTTP utilizado para a comunicação entre um servidor web e os seus clientes. Este é o objetivo dos programas seguintes.

O cliente web descrito na Secção 9.2 permitiu-nos explorar parte do protocolo HTTP.

Image

Na sua forma mais simples, as trocas cliente/servidor são as seguintes:

  • o cliente abre uma ligação à porta 80 no servidor web
  • e faz um pedido de um documento
  • o servidor web envia o documento solicitado e encerra a ligação
  • o cliente fecha então a conexão

O cliente pode assumir várias formas: texto HTML, uma imagem, um vídeo, etc. Pode ser um documento existente (documento estático) ou um documento gerado dinamicamente por um script (documento dinâmico). Neste último caso, referimo-nos à programação web. O script para gerar documentos dinamicamente pode ser escrito em várias linguagens: PHP, Python, Perl, Java, Ruby, C#, VB.NET, etc.

Aqui, utilizamos PHP para gerar dinamicamente documentos de texto.

  • Em [1], o cliente estabelece uma ligação com o servidor, solicita um script PHP e pode ou não enviar parâmetros para esse script
  • Em [2], o servidor web executa o script PHP utilizando o interpretador PHP. Este script gera um documento que é enviado ao cliente [3]
  • O servidor encerra a ligação. O cliente faz o mesmo.

O servidor web pode lidar com vários clientes ao mesmo tempo. Com o pacote de software WampServer, o servidor web é um servidor Apache, um servidor de código aberto da Apache Foundation (http://www.apache.org/). Nas aplicações seguintes, o WampServer deve ser iniciado. Isto ativa três componentes: o servidor web Apache, o sistema de gestão de bases de dados MySQL e o interpretador PHP.

Os scripts executados pelo servidor web serão escritos utilizando a ferramenta NetBeans. Até agora, escrevemos scripts PHP executados num ambiente de consola:

O utilizador utiliza a consola para solicitar a execução de um script PHP e receber os resultados.

Nas aplicações cliente/servidor que se seguem,

  • o script do cliente é executado num ambiente de consola
  • o script do servidor é executado num ambiente web

O script PHP do servidor não pode estar localizado em qualquer local do sistema de ficheiros. Isto porque o servidor web procura os documentos estáticos e dinâmicos que lhe são solicitados em locais especificados pela configuração. A configuração padrão do WampServer faz com que os documentos sejam procurados na pasta <WampServer>/www, onde <WampServer> é a pasta de instalação do WampServer. Assim, se um cliente web solicitar um documento D com o URL [http://localhost/D], o servidor web servirá o documento D localizado no caminho [<WampServer>/www/D].

Nos exemplos a seguir, colocaremos os scripts do servidor na pasta [www/web-examples]. Se um script do servidor se chamar S.php, ele será solicitado ao servidor web usando a URL [http://localhost/exemples-web/S.php]. O documento [<WampServer>/www/web-examples/S.php] será então servido.

Para criar um script do lado do servidor com o NetBeans , devemos proceder da seguinte forma:

  • Em [1], criamos um novo projeto
  • Em [2], selecionamos a categoria [PHP] e o projeto [Aplicação PHP]
  • em [3], nomeamos o projeto
  • Em [4], escolhemos uma pasta para o projeto
  • Em [5], especificamos que o script deve ser executado por um servidor web local (o URL do script terá o formato http://localhost/...). O servidor web local será o servidor web Apache do WampServer.
  • Em [6], especificamos a URL do projeto. Aqui, decidimos que um script chamado S.php no projeto será acedido através da URL [http://localhost/exemples-web/S.php]. Com base no exposto, isto significa que o caminho para o script S.php no sistema de ficheiros será [<WampServer>/www/web-examples/S.php]. É isto que está especificado em [7]. Aqui, especificamos que qualquer script S.php no projeto deve ser copiado para a estrutura de diretórios do servidor web Apache.
  • em [8], o novo projeto.

Vamos escrever um script de teste:

  • Em [1], criamos um primeiro script PHP no projeto [web-examples]
  • Em [2], atribuímos-lhe um nome
  • em [3], após criá-lo, atribuímos-lhe o seguinte conteúdo

Em seguida, o WampServer deve estar em execução.

  • Em [4], executamos o script web [example1.php]. O NetBeans irá então abrir o navegador predefinido do computador e instruí-lo a apresentar o URL [http://localhost/exemples-web/exemple1.php] [5]
  • Em [6], o navegador exibe o que o script do servidor enviou para o cliente.

Daqui em diante, iremos encontrar dois tipos de clientes web:

  • um navegador, tal como descrito acima. Observámos que o servidor web envia uma resposta no formato: cabeçalhos HTTP, linha em branco, texto. O navegador apresenta apenas o texto.
  • um script PHP que exibirá a resposta completa: cabeçalhos HTTP, linha em branco, texto.

Daqui em diante,

  • os scripts do lado do servidor serão escritos como [example1.php] acima
  • os scripts do lado do cliente serão escritos como os scripts de consola que escrevemos até agora.

10.1. Aplicação de data/hora cliente/servidor

10.1.1. O servidor (web_01)


<?php
// time: number of milliseconds since 01/01/1970
// date-time display format
// d: 2-digit day
// m: 2-digit month
// y: 2-digit year
// H: hour 0.23
// i : minutes
// s: seconds
print date("d/m/y H:i:s",time());

Basicamente, o script PHP acima exibe a hora atual no ecrã. No entanto, quando executado por um servidor web, o fluxo 1 — que normalmente está associado ao ecrã — é redirecionado para a ligação que une o servidor ao seu cliente. Portanto, num contexto web, o script acima envia a hora atual como texto para o cliente.

Vamos executar este script no NetBeans:

  • Em [1], executamos o script. Em seguida, é aberto um navegador da Web.
  • Em [2], a URL solicitada pelo navegador da Web
  • Em [3], o texto enviado pelo script do servidor

O navegador do cliente utiliza o protocolo HTTP para comunicar com o servidor web. Já descrevemos a estrutura deste protocolo.

O cliente envia linhas de texto que podem ser divididas em três partes: cabeçalhos HTTP, uma linha em branco e o documento. O documento enviado ao servidor web é geralmente vazio ou consiste num conjunto de parâmetros na forma parami=vali, onde vali é um valor introduzido pelo utilizador num formulário HTML.

A resposta do servidor tem a mesma estrutura: cabeçalhos HTTP, uma linha em branco e um documento, sendo que, desta vez, o documento é aquele solicitado pelo navegador do cliente. Se o cliente tiver enviado parâmetros, o documento devolvido depende geralmente desses parâmetros.

Com o navegador Firefox, é possível visualizar os dados efetivamente trocados entre o cliente e o servidor web. Existe um complemento do Firefox chamado Firebug que permite acompanhar essa troca de dados. O Firebug está disponível no URL [https://addons.mozilla.org/fr/firefox/addon/firebug/]. Se utilizar o navegador Firefox para visitar este URL, pode descarregar o plugin Firebug. Passaremos a assumir que o plugin Firebug já foi descarregado e instalado. É acessível através de uma opção no menu do Firefox:

 

Uma janela do Firebug abre-se dentro da janela do navegador Firefox. Esta janela possui, por sua vez, um menu:

 

Para visualizar as trocas entre o cliente e o servidor durante um pedido HTTP, introduzimos o URL [http://localhost/exemples-web/web_01.php] no navegador Firefox. A janela do Firebug fica então repleta de informações:

Acima está um resumo das trocas entre o cliente e o servidor:

  • [1]: O cliente enviou a solicitação HTTP: GET /exemples-web/web_01.php HTTP/1.1 para solicitar o documento [web01.php]
  • [2]: O servidor enviou a resposta: HTTP/1.1 200 OK, indicando que encontrou o documento solicitado.

O Firebug permite-lhe visualizar as trocas completas. Basta «expandir» o URL:

Acima, vemos os cabeçalhos HTTP trocados entre o cliente (Pedido) e o servidor (Resposta). É possível obter o código-fonte da troca, ou seja, as linhas de texto efetivamente trocadas [1]. Obtemos então o seguinte código-fonte:

 

Para escrever um script do lado do cliente para o servidor web, basta replicar o comportamento do navegador. Após estabelecer uma ligação com o servidor, o script do lado do cliente poderia enviar as 8 linhas da solicitação apresentadas acima. Na verdade, nem tudo é essencial, e enviaremos apenas as três linhas seguintes:

1
2
3
GET /exemples-web/web01.php HTTP/1.1
Host: localhost
Connection: close
  • Linha 1: especifica o documento solicitado e o protocolo HTTP utilizado
  • Linha 2: fornece o nome do host do script do cliente
  • Linha 3: indica que, após a troca, o cliente encerrará a ligação com o servidor

Vejamos agora a resposta do servidor. Sabemos que foi gerada pelo script PHP [web_01.php]. Acima, vemos os cabeçalhos HTTP da resposta. O código do script [web01.php] mostra que não foi ele a gerá-los. Recordemos a configuração do script do servidor:

Foi o servidor web que gerou os cabeçalhos HTTP da resposta. O script do servidor pode gerá-los por si próprio. Veremos um exemplo disso um pouco mais adiante.

Mencionámos que a resposta do servidor web tem o seguinte formato: cabeçalhos HTTP, linha em branco, documento. Se o documento for um documento de texto, podemos visualizá-lo no separador [Resposta] do Firebug:

 

Esta resposta foi gerada pelo script [web_01.php].

10.1.2. Um cliente (client1_web_01)

Vamos agora escrever um script de cliente para o serviço anterior. Sabemos que o cliente deve:

  • abrir uma ligação com o servidor web
  • enviar o texto: cabeçalhos HTTP, linha em branco
  • ler a resposta completa do servidor até que o servidor feche a sua ligação com o cliente
  • fechar a ligação com o servidor

O script do cliente é executado num ambiente de consola do NetBeans:

  • em [1], o script do cliente [client1_web_01.php] está incluído no projeto NetBeans [examples]
  • em [2], as propriedades do projeto NetBeans [examples]
  • em [3], o projeto NetBeans [examples] é executado no modo «linha de comando», ao qual também nos referimos como modo «consola».

O código do script do cliente é o seguinte:


<?php
 
// data
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_01.php";
// open a connection on port 80 of $HOTE
$connexion = fsockopen($HOTE, $PORT);
// mistake?
if (!$connexion) {
  print "Erreur : $erreur\n";
  exit;
}
// protocol HTTP headers must end with an empty line
// GET
fputs($connexion, "GET $urlServeur HTTP/1.1\n");
// Host
fputs($connexion, "Host: localhost\n");
// Connection
fputs($connexion,"Connection: close\n");
// blank line
fputs($connexion,"\n");
// the server will now respond on channel $connexion. It will send all
// then close the channel. The client therefore reads everything that arrives from $connexion
// until the channel closes
while ($ligne = fgets($connexion, 1000)) {
  print "$ligne";
}//while
// the customer in turn closes the connection
fclose($connexion);
// end
exit;

Comentários

  • linha 8: abertura de uma ligação ao servidor
  • linha 16: comando HTTP GET
  • linha 18: comando HTTP Host
  • linha 20: comando HTTP Connection
  • linha 22: linha vazia
  • Linhas 26–28: Ler todas as linhas de texto enviadas pelo servidor até que este feche a ligação.
  • linha 30: o cliente encerra a ligação

Resultados

A execução do script do cliente produz os seguintes 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

Comentários

  • Linhas 1–7: a resposta HTTP do servidor web.
  • Linha 8: a linha vazia que sinaliza o fim dos cabeçalhos HTTP
  • Linhas 9 e seguintes: o documento. Aqui, trata-se de um texto simples que representa a data e a hora atuais. Este é o texto escrito pelo script PHP para gerar a saída n.º 1.
  • Linha 1: O servidor responde que encontrou o documento solicitado.
  • Linha 2: a data e hora atuais do servidor
  • Linha 3: Identidade do servidor Web
  • Linha 4: indica que o documento que se segue é gerado por um script PHP
  • Linha 5: número de caracteres no documento
  • Linha 6: O servidor indica que, após enviar o documento, irá encerrar a ligação
  • Linha 7: indica que o documento enviado pelo servidor é texto no formato HTML. Isto está incorreto aqui. O documento é texto simples. Quando o documento não está no formato HTML, cabe ao script PHP indicar isso. Não o fizemos aqui.

10.1.3. Um segundo cliente (client2_web_01)

O cliente anterior exibiu tudo o que o servidor web lhe enviou. Na prática, geralmente ignoramos os cabeçalhos HTTP na resposta e focamo-nos no corpo do documento. Aqui, queremos recuperar a data e a hora enviadas pelo script PHP do lado do servidor. Iremos recuperar esta informação utilizando uma expressão regular.


<?php
 
// retrieve information sent by a web server
// data
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_01.php";
// open a connection on port 80 of $HOTE
$connexion = fsockopen($HOTE, $PORT);
// mistake?
if (!$connexion) {
  print "Erreur : $erreur\n";
  exit;
}
// protocol HTTP headers must end with an empty line
// GET
fputs($connexion, "GET $urlServeur HTTP/1.1\n");
// Host
fputs($connexion, "Host: localhost\n");
// Connection
fputs($connexion,"Connection: close\n");
// blank line
fputs($connexion,"\n");
// the server will now respond on channel $connexion. It will send all
// then close the channel. The client therefore reads everything that arrives from $connexion
// until it finds the line it's looking for in the form dd/mm/yy 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)) {
     // we retrieve the # fields
    array_shift($champs); // e// removes the 1st element from the array fields
     // we retrieve the 6 fields in 6 variables
    list($j, $m, $a, $h, $i, $s) = $champs;
     // result display
    print "\ndateheure=[$j,$m,$a,$h,$i,$s]\n";
  }////if
}//while
// the customer in turn closes the connection
fclose($connexion);
// end
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. Recuperação pelo servidor dos parâmetros enviados pelo cliente

No protocolo HTTP, um cliente dispõe de dois métodos para enviar parâmetros ao servidor web:

  • solicita a URL do serviço no formulário

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

onde os valores válidos devem primeiro ser codificados para que determinados caracteres reservados sejam substituídos pelos seus valores hexadecimais.

  • solicita o URL do serviço na forma

POST url HTTP/1.0

depois, entre os cabeçalhos HTTP enviados ao servidor, inclui o seguinte cabeçalho:

Content-length=N

O resto dos cabeçalhos enviados pelo cliente terminam com uma linha em branco. Pode então enviar os seus dados na forma

val1&param2=val2&param3=val3…

onde os valores válidos devem, tal como no método GET, ser codificados previamente. O número de caracteres enviados ao servidor deve ser N, sendo N o valor declarado no cabeçalho

Content-length=N

O script PHP que recupera os parâmetros parami anteriores enviados pelo cliente obtém os seus valores a partir da matriz:

  • $_GET["parami"] para um pedido GET
  • $_POST["parami"] para uma solicitação POST

10.2.1. O cliente GET (client1_web_02)

O script PHP abaixo envia três parâmetros [last_name, first_name, age] para o servidor.


<?php
 
// client: sends firstname,lastname,age to the server using the GET method
// data
$HOTE = "localhost";
$PORT = 80;
$URL = "/exemples-web/web_02.php";
list($prenom, $nom, $age) = array("jean-paul", "de la hûche", 45);
// web server connection
$connexion = fsockopen($HOTE, $PORT);
// return if error
if (!$connexion) {
  print "Echec de la connexion au site ($HOTE,$PORT) : $erreur";
  exit;
}//if
// information sent to server PHP
// information is encoded
$infos = "prenom=" . urlencode(utf8_decode($prenom)) . "&nom=" . urlencode(utf8_decode($nom)) . "&age=" . urlencode("$age");
// console monitoring
print "infos envoyées au serveur (GET)=$infos\n";
print "URL demandée=[$URL?$infos]\n\n";
// protocol HTTP headers must end with an empty line
// GET
fputs($connexion, "GET $URL?$infos HTTP/1.1\n");
// Host
fputs($connexion, "Host: localhost\n");
// Connection
fputs($connexion,"Connection: close\n");
// blank line
fputs($connexion,"\n");
// the server will now respond on channel $connexion. It will send all
// then close the channel. The client reads everything from $connexion until the channel is closed
while ($ligne = fgets($connexion, 1000))
  print "$ligne";
// the customer in turn closes the connection
fclose($connexion);

Comentários

  • linha 7: URL do script do servidor
  • linha 8: valores dos 3 parâmetros
  • linha 10: abre uma ligação ao servidor web
  • linha 18: codificação dos 3 parâmetros. Estamos a trabalhar num script escrito no NetBeans com codificação de caracteres UTF-8. Por conseguinte, os valores dos 3 parâmetros da linha 8 são codificados em UTF-8. A função utf8_decode converte a sua codificação para ISO-8859-1. Depois de feito isto, podem ser codificados para o URL. Todos os caracteres não alfabéticos são substituídos por %xx, em que xx é o valor hexadecimal do caractere. Os espaços são substituídos pelo sinal +.
  • Linha 24: A URL solicitada é $URL?$infos, onde $infos tem o formato last_name=val1&first_name=val2&age=val3.

10.2.2. O servidor (web_02)

O servidor simplesmente exibe o que recebe.


<?php
 
// error management
ini_set("display_errors", "off");
 
// server retrieves information sent by the client
// here firstname=P&lastname=N&age=A
// this information is automatically available in the
// $_GET['prenom'], $_GET['nom'], $_GET['age']
// we send them back to the customer
 
// uTF-8 header
header("Content-Type: text/plain; charset=utf-8");
 
// parameters sent to server
$prenom = isset($_GET['prenom']) ? $_GET['prenom'] : "";
$nom = isset($_GET['nom']) ? $_GET['nom'] : "";
$age = isset($_GET['age']) ? $_GET['age'] : "";
 
// customer response
$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;

Comentários

  • linha 13: define o cabeçalho HTTP "Content-Type". Por predefinição, o servidor web envia o cabeçalho
Content-Type: text/html

, o que indica que a resposta é texto no formato HTML. Aqui, a resposta será texto sem formatação com caracteres codificados em UTF-8:

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

Os cabeçalhos HTTP devem ser enviados antes da resposta do servidor. Portanto, no código acima, a chamada à função de cabeçalho deve vir antes de quaisquer instruções de impressão.

  • Linhas 16–18: Recuperamos os três parâmetros da matriz $_GET.
  • Linha 21: Construímos a string que será enviada como resposta ao cliente. Certos caracteres têm significados especiais em HTML e devem ser substituídos por entidades HTML para serem exibidos. htmlspecialchars($string) substitui todos esses caracteres pelos seus equivalentes na $string. Por exemplo, o caractere $ torna-se &amp. Depois, como especificámos na linha 13 que a resposta seria texto UTF-8, codificamos os valores recuperados em UTF-8.
  • Linha 25: A resposta é enviada ao cliente

Teste 1

Vamos executar o script [web_02] a partir do NetBeans. Um navegador será então aberto para exibir o URL [http://localhost/exemples-web/web_02.php]:

  • Em [1], o navegador exibe a URL [http://localhost/exemples-web/web_02.php]. Como não anexámos parâmetros a esta URL, o servidor respondeu com parâmetros vazios. Recorde-se que a resposta do servidor é aquela escrita com a instrução print.
  • Em [2], acrescentamos parâmetros à URL. Desta vez, o script do servidor devolve-os corretamente.

Note que o NetBeans não é necessário para executar um script de servidor. Basta introduzir a URL do script de servidor num navegador para o executar.

Teste 2

Executamos o cliente [client1_web_02.php] no NetBeans. Recebemos a seguinte resposta:

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]
  • Linha 1: Codificação dos 3 parâmetros. Podemos ver que o caractere û se tornou %FB.
  • linha 12: resposta do servidor

10.2.3. O cliente POST (client2_web_03)

Um cliente HTTP envia a seguinte sequência de texto para o servidor web: cabeçalhos HTTP, linha em branco, documento. No cliente anterior, esta sequência era a seguinte:

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

Não havia nenhum documento. Existe outra forma de transmitir parâmetros, conhecida como método POST. Neste caso, a sequência de texto enviada ao servidor web é a seguinte:

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

Desta vez, os parâmetros que foram incluídos nos cabeçalhos HTTP para o cliente GET fazem parte do documento enviado após os cabeçalhos no cliente POST.

O script do cliente POST é o seguinte:


<?php
 
// client: sends firstname,lastname,age to the server using the POST method
// data
$HOTE = "localhost";
$PORT = 80;
$URL = "/exemples-web/web_03.php";
list($prenom, $nom, $age) = array("jean-paul", "de la hûche", 45);
// web server connection
$connexion = fsockopen($HOTE, $PORT);
// return if error
if (!$connexion) {
  print "Echec de la connexion au site ($HOTE,$PORT) : $erreur";
  exit;
}//if
// information sent to server PHP
// information is encoded
$infos = "prenom=" . urlencode(utf8_decode($prenom)) . "&nom=" . urlencode(utf8_decode($nom)) . "&age=" . urlencode("$age");
print "client : infos envoyées au serveur (POST) : $infos\n";
// connect to the URL $URL by posting (POST) parameters to it
// protocol HTTP headers must end with an empty line
// POST
fputs($connexion, "POST $URL HTTP/1.1\n");
// Host
fputs($connexion, "Host: localhost\n");
// Connection
fputs($connexion,"Connection: close\n");
// Content-type
fputs($connexion, "Content-type: application/x-www-form-urlencoded\n");
// Content-length
// send the size (number of characters) of the information to be sent
fputs($connexion, "Content-length: " . strlen($infos) . "\n");
// send an empty line
fputs($connexion, "\n");
// we send the news
fputs($connexion, $infos);
// the server will now respond on channel $connexion. It will send all
// then close the channel. The client reads everything that arrives from $connexion
// until the channel closes
while ($ligne = fgets($connexion, 1000))
  print "$ligne";
// the customer in turn closes the connection
fclose($connexion);

Comentários

  • Linha 7: O URL do serviço web ao qual o cliente POST se irá ligar. Este serviço web será descrito em breve.
  • Linha 8: os parâmetros a enviar para o serviço web
  • Linha 10: Ligação ao servidor web
  • Linha 18: codificação dos parâmetros a enviar para o serviço web
  • Linha 23: Comando HTTP POST
  • Linha 25: Comando HTTP Host
  • linha 27: cabeçalho HTTP Connection
  • linha 29: cabeçalho HTTP Content-Type. Já nos deparámos com este cabeçalho HTTP. Está presente sempre que um documento é enviado. Um servidor web que envia um documento HTML utiliza o HTTP
Content-type : text/html

Se enviar texto não formatado, utiliza o cabeçalho HTTP

Content-type : text/plain

O nosso cliente POST envia um documento que consiste num texto no formato param1=val1&param2=val2&.... Este tipo de documento tem o tipo application/x-www-form-urlencoded. Não vamos explicar porquê, pois isso exigiria explicar o que é um formulário web.

  • Linha 32: Diretiva Content-length. Já nos deparámos com este cabeçalho HTTP. Está presente sempre que um documento é enviado. Indica o número de bytes no documento.
  • Linha 34: A linha vazia que indica o fim dos cabeçalhos HTTP
  • Linha 36: Envio dos parâmetros
  • Linhas 40–41: Leitura da resposta completa do servidor
  • Linha 43: Fechamento da conexão

10.2.4. O servidor (web_03)

O serviço web [web_03] faz o mesmo que o serviço web [web_02]. Ele lê os parâmetros enviados pelo cliente POST e os reenvia para o cliente. O seu código é o seguinte:


<?php
 
// error management
ini_set("display_errors", "off");
// uTF-8 header
header("Content-Type: text/plain; charset=utf-8");
 
// server retrieves information sent by the client
// here firstname=P&lastname=N&age=A
// this information is automatically available in the
// $_POST['prenom'], $_POST['nom'], $_POST['age']
// we send them back to the customer
// parameters sent to server
$prenom = isset($_POST['prenom']) ? $_POST['prenom'] : "";
$nom = isset($_POST['nom']) ? $_POST['nom'] : "";
$age = isset($_POST['age']) ? $_POST['age'] : "";
// customer response
$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;

Comentários

  • linhas 14–16: Os parâmetros enviados por um cliente POST ficam disponíveis na matriz $_POST para o serviço web que os recebe.
  • linha 6: Cabeçalho HTTP Content-Type. Poderá ficar surpreendido por não encontrar o cabeçalho HTTP Content-Length nos cabeçalhos HTTP, que indica o tamanho do documento enviado de volta ao cliente. Vimos que o servidor web envia cabeçalhos HTTP por predefinição. O cabeçalho Content-Length é um deles.

Resultados

Assim que o script do servidor é escrito no NetBeans, fica imediatamente disponível através do servidor Apache WampServer. Lembre-se de que isto é conseguido através da configuração (ver parágrafo 10). Iniciamos o cliente, que consulta o servidor, e recebemos então a seguinte resposta:

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]
  • linhas 2-10: a resposta do servidor
  • linhas 2-8: os cabeçalhos HTTP
  • linha 10: o documento
  • linha 6: o cabeçalho HTTP Content-Length. Uma vez que este cabeçalho não foi gerado pelo script do servidor, foi gerado pelo servidor web.
  • Linha 8: o único cabeçalho gerado pelo script do servidor

10.3. Recuperação de variáveis de ambiente do servidor web

Um script de servidor é executado num ambiente web ao qual tem acesso. Este ambiente está armazenado no dicionário $_SERVER. Primeiro, escrevemos uma aplicação de servidor que envia o conteúdo deste dicionário aos seus clientes.

10.3.1. O servidor (web_04)


<?php
 
// error management
ini_set("display_errors", "off");
// uTF-8 header
header("Content-Type: text/plain; charset=utf-8");
 
// returns to the client the list of variables available in the server environment
foreach ($_SERVER as $clé => $valeur) {
  print "[$clé,$valeur]\n";
}
  • Os pares (chave, valor) do dicionário $_SERVER são enviados aos clientes.

O resultado obtido quando o cliente é um navegador web é o seguinte:

Image

Eis o significado de algumas das variáveis (para Windows. No Linux, seriam diferentes):

HTTP_CMDE
CMDE representa o cabeçalho HTTP enviado pelo cliente. Temos acesso a todos estes cabeçalhos.
PATH
O caminho para os executáveis na máquina onde o script do servidor está a ser executado
COMSPEC
O caminho para o interpretador de comandos do DOS
PATHEXT
as extensões dos ficheiros executáveis
WINDIR
a pasta de instalação do Windows
SERVER_SIGNATURE
a assinatura do servidor web. Não há nada aqui.
SERVER_SOFTWARE
o tipo de servidor web
SERVER_NAME
O nome de Internet do servidor web
PORTA_DO_SERVIDOR
A porta de escuta do servidor web
SERVER_ADDR
o endereço IP do servidor web
REMOTE_ADDR
o endereço IP do cliente. Neste caso, o cliente estava na mesma máquina que o servidor.
REMOTE_PORT
a porta de comunicação do cliente
DOCUMENT_ROOT
a raiz da árvore de diretórios dos documentos servidos pelo servidor web
SERVER_ADMIN
o endereço de e-mail do administrador do servidor web
SCRIPT_FILENAME
o caminho completo do script do servidor
SERVER_PROTOCOL
a versão do protocolo HTTP utilizada pelo servidor web
REQUEST_METHOD
o método HTTP utilizado pelo cliente. Existem quatro: GET, POST, PUT, DELETE
QUERY_STRING
os parâmetros enviados com um pedido GET /url?parâmetros
REQUEST_URI
A URL solicitada pelo cliente. Se o navegador solicitar a URL http://machine[:port]/uri, REQUEST_URI será uri
SCRIPT_NAME
$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'] . $_SERVER['SCRIPT_NAME']

10.3.2. O cliente (client1_web_04)

O cliente simplesmente exibe tudo o que o servidor lhe envia.


<?php
 
// data
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_04.php";
// open a connection on port 80 of $HOTE
$connexion = fsockopen($HOTE, $PORT);
// mistake?
if (!$connexion) {
  print "Erreur : $erreur\n";
  exit;
}
 
// connect to the Web server on a URL
// protocol HTTP headers must end with an empty line
// GET
fputs($connexion, "GET $urlServeur HTTP/1.1\n");
// Host
fputs($connexion, "Host: localhost\n");
// Connection
fputs($connexion,"Connection: close\n");
// blank line
fputs($connexion,"\n");
// the server will now respond on channel $connexion. It will send all
// then close the channel. The client therefore reads everything that arrives from $connexion
// until the channel closes
while ($ligne = fgets($connexion, 1000)) {
  print "$ligne";
}//while
// the customer in turn closes the connection
fclose($connexion);
// end
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. Gestão de sessões web

Nos exemplos anteriores de cliente/servidor, o processo era o seguinte:

  • o cliente abre uma ligação à porta 80 no servidor web
  • envia a sequência de texto: cabeçalhos HTTP, linha em branco, [documento]
  • em resposta, o servidor envia uma sequência do mesmo tipo
  • o servidor encerra a ligação com o cliente
  • o cliente encerra a ligação ao servidor

Se o mesmo cliente fizer uma nova solicitação ao servidor web pouco tempo depois, uma nova conexão é estabelecida entre o cliente e o servidor. O servidor não consegue distinguir se o cliente que está se conectando já visitou o site anteriormente ou se esta é uma solicitação pela primeira vez. Entre conexões, o servidor “esquece” o seu cliente. Por esse motivo, diz-se que o protocolo HTTP é um protocolo sem estado. No entanto, é útil que o servidor se lembre dos seus clientes. Por exemplo, se uma aplicação for segura, o cliente enviará ao servidor um nome de utilizador e uma palavra-passe para se autenticar. Se o servidor «esquecer» o seu cliente entre conexões, o cliente teria de se autenticar a cada nova conexão, o que não é viável.

Para rastrear um cliente, o servidor procede da seguinte forma: quando um cliente faz uma solicitação inicial, o servidor inclui um identificador na sua resposta, que o cliente deve então enviar de volta a cada solicitação subsequente. Usando esse identificador — que é único para cada cliente —, o servidor pode reconhecer o cliente. Ele pode então manter um registo desse cliente na forma de um ficheiro associado exclusivamente ao identificador do cliente.

Tecnicamente, é assim que funciona:

  • Na resposta a um novo cliente, o servidor inclui o cabeçalho HTTP Set-Cookie: Key=Identifier. Faz isso apenas na primeira solicitação.
  • Nas solicitações subsequentes, o cliente enviará o seu identificador através do cabeçalho HTTP Cookie: Key=Identifier para que o servidor possa reconhecê-lo.

Pode-se perguntar como é que o servidor sabe que está a lidar com um novo cliente em vez de um cliente recorrente. É a presença do cabeçalho HTTP Cookie nos cabeçalhos HTTP do cliente que o indica. Para um novo cliente, este cabeçalho está ausente.

O conjunto de ligações de um determinado cliente é chamado de sessão.

10.4.1. O ficheiro de configuração

Para que a gestão de sessões funcione corretamente com o PHP, deve verificar se está devidamente configurado. No Windows, o ficheiro de configuração é o PHP.ini. Dependendo do contexto de execução (consola, web), o ficheiro de configuração [PHP.ini] deve estar localizado em diretórios diferentes. Para os encontrar, utilize o seguinte script:

1
2
3
4
<?php

// infos PHP
phpinfo();

Linha 4: A função phpinfo() fornece informações sobre o interpretador PHP que está a executar o script. Em particular, devolve o caminho para o ficheiro de configuração [PHP.ini] que está a ser utilizado.

Num ambiente de consola, obterá um resultado semelhante ao seguinte:

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)

Linha 2: O ficheiro de configuração principal é c:\windows\PHP.ini

Linha 3: Um ficheiro de configuração secundário é C:\DBServers\wamp21\bin\PHP\php5.3.5\PHP.ini. Permite-lhe modificar determinadas opções de configuração no ficheiro de configuração principal.

Num ambiente web, obtém-se o seguinte resultado:

Image

O ficheiro de configuração secundário aqui não é o mesmo que no ambiente de consola. É este último que iremos examinar. Neste ficheiro, existe uma secção de sessão:

[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
  • Linha 1: Os dados da sessão do cliente são guardados num ficheiro
  • Linha 3: o diretório onde os dados da sessão são guardados. Se este diretório não existir, não é reportado qualquer erro e a gestão da sessão não funciona.
  • Linhas 4-5: indicam que o ID da sessão é gerido pelos cabeçalhos HTTP Set-Cookie e Cookie
  • linha 6: o cabeçalho Set-Cookie terá o formato Set-Cookie: PHPSESSID=session_id
  • Linha 7: Uma sessão do cliente não é iniciada automaticamente. O script do servidor deve solicitá-la explicitamente utilizando a função session_start().

10.4.2. Servidor 1 (web_05)

A gestão do ID de sessão é transparente para um serviço web. Este identificador é gerido pelo servidor web. Um serviço web acede à sessão do cliente através da função session_start(). A partir desse momento, o serviço web pode ler e escrever na sessão do cliente através da matriz $_SESSION. O código seguinte demonstra a gestão de sessão para três contadores.


<?php
 
// error management
ini_set("display_errors", "off");
// uTF-8 header
header("Content-Type: text/plain; charset=utf-8");
 
// log in
session_start();
// on met 3 variables en session
if (!isset($_SESSION['N1'])) {
  $_SESSION['N1'] = 0;
}
if (!isset($_SESSION['N2'])) {
  $_SESSION['N2'] = 10;
}
if (!isset($_SESSION['N3'])) {
  $_SESSION['N3'] = 100;
}
// incrementing the 3 variables
$_SESSION['N1']++;
$_SESSION['N2']++;
$_SESSION['N3']++;
// sending information to the customer
print "N1=".$_SESSION['N1']."\n";
print "N2=".$_SESSION['N2']."\n";
print "N3=".$_SESSION['N3']."\n";
// end of session
session_close();
  • linha 9: início de uma sessão do cliente
  • linhas 11-13: a matriz $_SESSION é um dicionário chave-valor. Os dados armazenados neste dicionário persistem entre os pedidos do mesmo cliente. Funciona como a memória do cliente no servidor.
  • linhas 11-19: se os três contadores N1, N2, N3 não estiverem na sessão, são adicionados a ela.
  • linhas 21–23: são incrementados em um
  • linhas 25–27: os seus valores são enviados para o cliente

Na relação cliente/servidor, a gestão da sessão do cliente no servidor depende de ambas as partes, o cliente e o servidor:

  • o servidor é responsável por enviar um identificador ao cliente na sua primeira solicitação
  • o cliente é responsável por enviar este identificador de volta com cada nova solicitação. Se não o fizer, o servidor assumirá que se trata de um novo cliente e gerará um novo identificador para uma nova sessão.

Resultados

Utilizamos um navegador web como cliente. Por predefinição (na verdade, por configuração), o navegador reenvia efetivamente ao servidor os identificadores de sessão que o servidor lhe envia. À medida que as solicitações são feitas, o navegador receberá os três contadores enviados pelo servidor e verá os seus valores a aumentar.

  • Em [1], a primeira solicitação ao serviço web [web_05]
  • Em [2], a terceira solicitação mostra que os contadores estão, de facto, a ser incrementados. Os valores dos contadores estão, de facto, a ser armazenados entre as solicitações.

Vamos usar o Firebug para visualizar os cabeçalhos HTTP trocados entre o servidor e o cliente. Fechamos o Firefox para encerrar a sessão atual com o servidor, reabrimos o navegador e ativamos o Firebug. Solicitamos o serviço [web_05]:

Acima, vemos o ID da sessão enviado pelo servidor na sua resposta ao primeiro pedido do cliente. Ele utiliza o cabeçalho HTTP Set-Cookie.

Vamos fazer uma nova solicitação atualizando (F5) a página no navegador da Web:

Aqui, vamos notar duas coisas:

  • O navegador da Web reenvia o ID da sessão com o cabeçalho HTTP Cookie.
  • Na sua resposta, o serviço web já não inclui este identificador. Cabe agora ao cliente enviá-lo em cada uma das suas solicitações.

10.4.3. Cliente 1 (client1_web_05)

Vamos agora escrever um script do lado do cliente com base no script anterior do lado do servidor. Na sua gestão de sessões, deve comportar-se como o navegador web:

  • Na resposta do servidor à sua primeira solicitação, ele deve encontrar o ID de sessão que o servidor lhe envia. Ele sabe que o encontrará no cabeçalho HTTP Set-Cookie.
  • Para cada pedido subsequente, deve enviar o identificador que recebeu de volta ao servidor. Fá-lo-á utilizando o cabeçalho HTTP Cookie.

O código do cliente é o seguinte:


<?php
 
// data
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_05.php";
// tests
$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";
}
// end
exit;
 
function connecte($HOTE, $PORT, $urlServeur, $cookie) {
  // connects client to ($HOTE,$PORT,$urlServeur)
  // sends the $cookie cookie if it is non-empty
  // displays all lines received in response
  // open a connection on port 80 of $HOTE
  $connexion = fsockopen($HOTE, $PORT);
  // mistake?
  if (!$connexion)
    return array("erreur lors de la connexion au serveur ($HOTE, $PORT)");
  // connect to $urlserveur
  // protocol HTTP headers must end with an empty line
  // GET
  fputs($connexion, "GET $urlServeur HTTP/1.1\n");
  // Host
  fputs($connexion, "Host: localhost\n");
  // Connection
  fputs($connexion, "Connection: close\n");
  // send cookie if non-empty
  if ($cookie) {
    fputs($connexion, "Cookie: $cookie\n");
  }////if
  // send empty line
  fputs($connexion, "\n");
  // the web server response is displayed
  // and take care to retrieve any cookies and Ni
  $N = "";
  while ($ligne = fgets($connexion, 1000)) {
    print "$ligne";
    // cookie - only on 1st response
    if (!$cookie) {
      if (preg_match("/^Set-Cookie: (.*?)\s*$/", $ligne, $champs)) {
        $cookie = $champs[1];
      }
    }
    // n1 value
    if (preg_match("/^N1=(.*?)\s*$/", $ligne, $champs))
      $N1 = $champs[1];
    // n2 value
    if (preg_match("/^N2=(.*?)\s*$/", $ligne, $champs))
      $N2 = $champs[1];
    // n3 value
    if (preg_match("/^N3=(.*?)\s*$/", $ligne, $champs))
      $N3 = $champs[1];
  }////while
  // close the connection
  fclose($connexion);
  // return
  return array("", $cookie, $N1, $N2, $N3);
}

Comentários

  • linhas 3-16: o programa principal
  • linhas 18–67: a função de ligação
  • Linhas 9–14: O cliente chama o servidor cinco vezes e exibe os valores sucessivos dos contadores N1, N2 e N3. Se a sessão for gerida corretamente, estes contadores devem ser incrementados em 1 a cada nova solicitação.
  • Linha 10: A função `connecte` utiliza os parâmetros `$HOTE`, `$PORT` e `$urlServeur` para ligar o cliente ao serviço web. O parâmetro `$cookie` representa o ID da sessão. Na primeira chamada, é uma cadeia de caracteres vazia. Nas chamadas subsequentes, é o ID da sessão enviado pelo servidor em resposta à primeira chamada do cliente. A função `connecte` devolve os valores dos três contadores `$N1`, `$N2`, `$N3`, o ID da sessão `$cookie` e quaisquer erros em `$erreur`.
  • Linha 18: A função `connecte` tem as características de um cliente HTTP padrão. Iremos apenas comentar as novas funcionalidades.
  • Linhas 30–40: Envio de cabeçalhos HTTP.
  • Linhas 36–38: Se o ID da sessão for conhecido, este é enviado para o servidor
  • Linhas 44–66: processamento de todas as linhas de texto enviadas pelo servidor
  • Linhas 47–51: Se o ID da sessão ainda não tiver sido recuperado, é recuperado do cabeçalho HTTP Set-Cookie utilizando uma expressão regular.
  • Linhas 53–54: o contador N1 também é obtido utilizando uma expressão regular
  • linhas 56–57, 59–60: o mesmo para os contadores N2 e N3
  • linha 63: Fechar a ligação ao servidor.
  • Linha 65: Os resultados são devolvidos como uma matriz.

Resultados

A execução do script do cliente faz com que o seguinte seja exibido na consola do 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]
  • Linha 5: Na sua primeira resposta, o servidor envia o ID da sessão. Nas respostas subsequentes, já não o envia.
  • É evidente que o servidor web mantém os valores de (N1, N2, N3) ao longo das solicitações do cliente. Isto é chamado de rastreamento de sessão.

Os dois exemplos seguintes mostram que também é possível guardar os valores de uma matriz ou de um objeto.

10.4.4. Servidor 2 (web_06)

O seguinte script de servidor demonstra que uma matriz ou um dicionário podem ser armazenados numa sessão.


<?php
 
// error management
ini_set("display_errors", "off");
// uTF-8 header
header("Content-Type: text/plain; charset=utf-8");
 
// log in
session_start();
// save a table and a dictionary
// initialize or modify the table
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;
  }
}
// initialize or modify the dictionary
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);
}
// sending information to the customer
print "tableau=" . join(",", $_SESSION['tableau']) . "\n";
print "dico=";
foreach ($_SESSION['dico'] as $clé => $valeur) {
  print "($clé,$valeur) ";
}
print "\n";

Comentários

  • linhas 17-19: é criada inicialmente uma matriz, caso ainda não exista
  • linhas 12–15: se já existir, os seus elementos são incrementados em 1
  • linha 27: um dicionário com valores numéricos é inicializado, caso ainda não exista
  • linhas 22-25: se já estiver na sessão, os seus valores numéricos são incrementados em 1
  • linhas 30-35: a matriz e o dicionário são enviados para o cliente

10.4.5. Cliente 2 (client1_web_06)


<?php
 
// data
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_06.php";
// tests
$cookie = "";
for ($i = 0; $i < 5; $i++) {
  connecte($HOTE, $PORT, $urlServeur, $cookie);
}
// end
exit;
 
function connecte($HOTE, $PORT, $urlServeur, &$cookie) {
  // connects client to ($HOTE,$PORT,$urlServeur)
  // sends the $cookie cookie if it is non-empty
  // displays all lines received in response
  // the cookie is passed by reference to be shared between
  // the called program and the calling program
  // open a connection on the $PORT port of $HOTE
  $connexion = fsockopen($HOTE, $PORT);
  // mistake?
  if (!$connexion)
    return array("erreur lors de la connexion au serveur ($HOTE, $PORT)");
  // protocol HTTP headers must end with an empty line
  // GET
  fputs($connexion, "GET $urlServeur HTTP/1.1\n");
  // Host
  fputs($connexion, "Host: localhost\n");
  // Connection
  fputs($connexion, "Connection: close\n");
  // send cookie if non-empty
  if ($cookie) {
    fputs($connexion, "Cookie: $cookie\n");
  }
  // send empty line
  fputs($connexion, "\n");
  // the web server response is displayed
  // and we take care to recover any cookie
  while ($ligne = fgets($connexion, 1000)) {
    print "$ligne";
    // cookie - only on 1st response
    if (!$cookie) {
      if (preg_match("/^Set-Cookie: (.*?)\s*$/", $ligne, $champs)) {
        $cookie = $champs[1];
      }
    }
  }
  // close the connection
  fclose($connexion);
  // return
  return "";
}

O código do cliente é semelhante ao código do cliente já 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. Servidor 3 (web_07)

O seguinte script de servidor mostra que um objeto pode ser armazenado numa sessão.


<?php
 
// error management
ini_set("display_errors", "off");
// uTF-8 header
header("Content-Type: text/plain; charset=utf-8");
 
// log in
session_start();
// initialize or modify a Personne object
if (isset($_SESSION['personne'])) {
  $personne = $_SESSION['personne'];
  // increment age
  $personne->setAge($personne->getAge() + 1);
} else {
  // we define the person
  $_SESSION['personne'] = new Personne("paul", "langévin", 10);
}
// customer display
print "personne=".$_SESSION['personne']."\n";
// end
exit;
 
// ----------------------------------------------------------------
class Personne {
 
// class attributes
  private $prénom;
  private $nom;
  private $âge;
 
// getters and 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;
  }
 
// manufacturer
  function __construct($prénom, $nom, $âge) {
    // we go through sets
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }
 
// method toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }
 
}

Comentários

  • linha 17: Adicionamos um objeto Pessoa à sessão, caso ainda não exista.
  • linhas 11-15: se já estiver presente, aumentamos a sua idade em 1
  • linha 20: enviamos o objeto Pessoa para o cliente.

10.4.7. Cliente 3 (client1_web_07)


<?php
 
// data
$HOTE = "localhost";
$PORT = 80;
$urlServeur = "/exemples-web/web_07.php";
// tests
$cookie = "";
for ($i = 0; $i < 5; $i++) {
  connecte($HOTE, $PORT, $urlServeur, $cookie);
}//if
// end
exit;
 
function connecte($HOTE, $PORT, $urlServeur, &$cookie) {
...
}

Comentários

  • Linha 15: A função connect é idêntica à do script do 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]