Blog do TaQ

Como fazer lambança (era:Gasparzinho atacando no browser)

Publicado em Developer

Só estava faltando acender uma vela aqui, por que de resto eu já havia tentado de praticamente tudo. Um caso de assombração. Imaginemos uma tabela no banco de dados chamada PRODUTOS, com dois campos: ID e NOME. Imaginemos também que eu tenho um código como esse:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
   <head>
      <title></title>
      <meta http-equiv="Content-Type" content="text/html; 
charset=ISO-8859-1"/>
      <link rel="stylesheet" type="text/css" href=""/>
   </head>
   <body>
      <h1>Produto novo</h1>
      <?php
         $con = mysql_connect("localhost","taq","******");
         mysql_select_db("test");
         $rst = mysql_query("select case when max(id)+1 is null then 1 
else max(id)+1 end as next_id from produtos");
         $row = mysql_fetch_array($rst);
         $nid = $row["next_id"];
         mysql_free_result($rst);
         print "<h2>Novo id: $nid</h2>";

         mysql_query("insert into produtos values ($nid,'esse é o 
registro de $nid')");
      ?>
   </body>
</html>

Coisa ridícula de simples: toda vez que é executado esse script, é inserido um novo registro no banco de dados, com o valor do próximo ID (poderia ter usado um campo com autoincrement do MySQL aqui, mas no Oracle já teria que ser sequences, então deixa assim que está portável).

Até aí tudo bem. Agora vou rodar no Firefox e ir apertando F5 para ver o resultado ...

Produto novo
Novo id: 1
<F5>
Produto novo
Novo id: 3 (???)
<F5>
Produto novo
Novo id: 5 (?????)

Mas que diabos! Está pulando um! Vou dar uma olhada no banco de dados e ...

mysql> select * from produtos;
+----+------------------------+
| id | nome                   |
+----+------------------------+
|  1 | esse é o registro de 1 |
|  2 | esse é o registro de 2 |
|  3 | esse é o registro de 3 |
|  4 | esse é o registro de 4 |
|  5 | esse é o registro de 5 |
|  6 | esse é o registro de 6 |
+----+------------------------+
6 rows in set (0,00 sec)

Ouch! O que aconteceu? Bom, após MUITOS minutos quebrando a cabeça percebi uma coisa ... olhem no código ali em cima. Eu fiz um esqueleto no Vim para me retornar já algumas coisas para arquivos de extensão .html e .php, para eu completar depois com o que for necessário. Nesse caso, esqueci de informar alguma coisa lá em <link rel="stylesheet" type="text/css" href=""/>. Mas isso não é só o CSS? Aparentemente é, mas se eu retirar aquela linha (ou informar um nome de arquivo) e rodar o script com as F5 novamente ...

Produto novo
Novo id: 1
<F5>
Produto novo
Novo id: 2
<F5>
Produto novo
Novo id: 3

Verificando no banco de dados:

mysql> select * from produtos;
+----+------------------------+
| id | nome                   |
+----+------------------------+
|  1 | esse é o registro de 1 |
|  2 | esse é o registro de 2 |
|  3 | esse é o registro de 3 |
+----+------------------------+
6 rows in set (0,00 sec)

PUTZ, está certo! Detalhe, o fato acima só ocorreu com o Firefox, tanto no GNU/Linux como no Windows de uma máquina aqui do CPD. E por incrível que pareça no Konqueror deu esse lance estranho também e no IE rodou legal mesmo com a referência do CSS em branco! Sei que foi erro meu não ter preenchido o href, mas puxa, que pepino feio hein. Parece que ele recarrega a página ou sei lá o que. :-p

Alguém pode confirmar se acontece isso também em seus computadores?

Atualizado às 20:40: Alterei até o título depois dessa. Pressa é uma @#$@#$@! Depois de pensar um pouco de cuca mais fresca no ônibus (a patroa ficou com o carro hoje) a caminho de casa, caiu a ficha! Olhem só o que eu encontrei em Uniform Resource Identifiers (URI): Generic Syntax (mais sobre isso em Relative Uniform Resource Locators também):

4.2. Same-document References

A URI reference that does not contain a URI is a reference to the
current document. 

Errrr ... foi mal, gente. My mistake. Mas mais uma coisa para se saber (e tomar cuidado). O que ocorreu é que, tendo href="" (em branco), para o browser é traduzido que a localização do arquivo CSS é a página corrente, então ela é chamada novamente, o que acarreta a geração de um novo código. Então seria mais ou menos assim:

  1. Carrega página
  2. Executa o script (mostra o id 1)
  3. Tenta carregar o suposto arquivo CSS, que aponta para a mesma página, e cria o id 2
  4. Carrega página
  5. Executa o script (mostra o id 3)
  6. Tenta carregar o suposto arquivo CSS, que aponta para a mesma página, e cria o id 4
  7. ...

E por aí vai. Outra coisa interessante é que podemos ver que a estrutura da página é carregada primeiro (o (X)HTML), e depois é feito o carregamento da(s) folha(s) de estilo para a formatação visual da mesma. Fica fácil comprovar o carregamento usando:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
   <head>
      <title></title>
      <meta http-equiv="Content-Type" content="text/html; 
charset=ISO-8859-1"/>
      <link rel="stylesheet" type="text/css" href=""/>
   </head>
   <body>
      <h1>Produto novo</h1>
      <?php
         $con = mysql_connect("localhost","taq","******");
         mysql_select_db("test");
         $rst = mysql_query("select case when max(id)+1 is null then 1 
else max(id)+1 end as next_id from produtos");
         $row = mysql_fetch_array($rst);
         $nid = $row["next_id"];
         mysql_free_result($rst);

         $handle = fopen("/tmp/linkrel.txt","a");
         fwrite($handle,$nid." - ".date("d/m/Y H:i:s")."\n");
         fclose($handle);

         print "<h2>Novo id: $nid</h2>";

         mysql_query("insert into produtos values ($nid,'esse é o 
registro de $nid')");
      ?>
   </body>
</html>

Notem que toda a vez que a página é carregada eu insiro uma linha nova no arquivo /tmp/linkrel.txt. Podia ter olhado o log do Apache também. Dã. Dando uma olhada no conteúdo desse arquivo após uma única vez que eu carreguei a página, tenho:

1 - 11/01/2006 20:40:30
2 - 11/01/2006 20:40:30

Tchans. Olha a prova do carregamento duplo ali. Ah, e o IE é burrão (de novo). :-)


Tags:


Comentários

Sem nenhum comentário.

comments powered by Disqus

Twitter