Como assim? Mas eu digito uma letra e aparece uma janelinha me dando sugestões em tempo de digitação!
Pois é, o Google Suggest utiliza sim requisições assíncronas mas não podemos chamar de Ajax.
O famoso Ajax ficou conhecido devido ao uso do objeto XMLHttpRequest e o nosso amigo Google não utiliza este objeto para fazer suas requisições assíncronas, ele utiliza simplesmente o fato de chamar scripts externos.

Essa é uma técnica antiga muito utilizada quando o XMLHttpRequest não era tão famoso, o pessoal já se virava com seus scripts dinâmicos e iframes.

Como funciona internamente o Google Suggest?

Na primeira letra digitada ele faz a criação dinâmica de uma tag <script> já setando seu atributo src para http://clients1.google.com.br/complete/search?hl=pt-BR&q= sendo que o parâmetro q equivale ao valor do campo de busca.

Então por exemplo, vou buscar sobre “futebol”, assim que digito a letra “f”, ele faz todo esse processo apontando para http://clients1.google.com.br/complete/search?hl=pt-BR&q=f 

Na segunda letra digitada, visto que a tag <script> já foi criada anteriormente ele simplesmente muda o atributo src agora apontando para a nova url http://clients1.google.com.br/complete/search?hl=pt-BR&q=fu

Mas como através dessa requisição aparece a lista de sugestões ?

A resposta dessa requisição é simplesmente texto puro, mas por ser chamada na tag <script> ela acaba sendo reconhecida como JavaScript e sendo executada por quem a chamou, em nosso caso a página inicial do Google.
O retorno da requisição é simplesmente isso (Um exemplo para a busca da palavra fut):

window.google.ac.h(["fut",[["futebol ao vivo","5.880.000 resultados","0"],
["futebol","39.800.000 resultados","1"]

Somente essa linha de código é responsável por popular a lista de sugestões.

Segue abaixo uma screenshot no momento da “popularização” de dados:

Google Suggest internamente

Por que a Google não utilizou o XMLHttpRequest? Isso foi uma boa solução?

Não é interessante para o Google Suggest controlar o retorno da requisição, saber readyState, status, nada disso é interessante visto que uma requisição é feita a cada letra digitada, então cada requisição sobrescreve a outra.

Não estamos preocupados se a requisição foi feita corretamente, ele simplesmente faz a requisição e se concluir OK, se não der tempo de completar (Isso é bem provável pois se outra letra foi digitada uma nova requisição foi feita), então ele esquece a chamada anterior e vai para a próxima, assim até o usuário “descansar” o dedo e dar tempo para a requisição ser concluída, logo depois a conclusão será mostrado para o usuário os resultados de sua busca.

Outro ponto interessante é a compatibilidade entre browsers. Navegadores muito antigos não têm suporte a XMLHttpRequest e atuais como o IE podem desabilitar essa opção, então na minha opinião foi uma ótima sacada sendo que é garantido que rodará em praticamente qualquer navegador.

Ainda vejo muitos desenvolvedores tentando fazer duas requisições Ajax ao mesmo tempo e sofrendo muito com os problemas que isso pode causar. Lembrando que ao fazer a segunda requisição (junto com a primeira) você está simplesmente anulando a anterior. Outra opção é a criação de diversas instâncias de XMLHttpRequest, mas também não é viável devido a incompatibilidade e sobrecarregamento do browser.

Recentemente desenvolvi uma classe Ajax que suporta múltiplas requisições utilizando o conceito de fila e venho aqui compartilhar com vocês.
É uma classe simples responsável por encapsular toda a complexidade do Ajax (não que Ajax seja difícil, pelo contrário Ajax é muito fácil desde que seja utilizado corretamente).
Utilizando o padrão Singleton (lembrando que esse padrão não é 100% seguro em JavaScript) criamos uma única instância do objeto XMLHttpRequest para ser usado em todas as requisições.

Código de classe:
Clique aqui para ver

Modo de usar:

1 Ajax.doPost(teste.php, “param=1&param=2″, function()
2 {
3     if (Ajax.getInstance().readyState == 4)
4     {
5         if (Ajax.getInstance().status == 200)
6         {
7             alert(Ajax.getInstance().responseText);
8          }
9          Ajax.next();
10    }
11 });

Código de exemplo:
Clique aqui para ver

Basta chamar o método Ajax.doPost() para executar uma nova requisição sem se importar se a requisição anterior foi concluída ou não, pois o próximo doPost() somente será executado se a requisição anterior foi concluída.

Um dos grandes problemas que costumo ver no MySQL é o seguinte: “Quero inserir um registro em uma tabela, recuperar o ID que foi gerado pelo AUTO_INCREMENT e inserir registros em outras tabelas.”
Vejo por aí muitas soluções,  algumas interessantes e outras grandes gambiarras.
Esqueça MAX() +1 , ORDER BY chave DESC LIMIT 1, SHOW TABLE STATUS, entre outros…

Segue abaixo uma das melhores formas para se fazer isso:

1 INSERT INTO tabela (chave_primaria, coluna1, coluna2) VALUES (null, aaa, bbb);
2 SELECT LAST_INSERT_ID() INTO @ID;
3 INSERT INTO outratabela (chave_estrangeira, coluna3, coluna4) VALUES (@id, xxx, yyy);
4 INSERT INTO outratabela (chave_estrangeira, coluna3, coluna4) VALUES (@id, www, zzz);
5 INSERT INTO outratabela (chave_estrangeira, coluna3, coluna4) VALUES (@id, uuu, iiii);

Desta forma você tem 100% de eficiência na recuperação do ID, não tendo problemas em capturar um ID recém-inserido por outro usuário. Com LAST_INSERT_ID() você estará recuperando sempre o último ID gerado pela sua sessão.

Se você estiver usando INNODB engine, ainda pode trabalhar com transações adicionando os comandos START TRANSACTION e COMMIT para ter total integridade dos dados.

O Log4J

Com a famosa biblioteca Log4J é possível gerar diversos níveis de erro que vão desde simples informações de debug até erros de nível crítico. É possível também indicar como esses erros serão apresentados, você tem a opção de mostrá-los no console, armazená-los em arquivos ou até mesmo serem enviados por email.

Em alguns casos essa opção de envio por email é muito importante, principalmente quando é usada para informar ao programador ou administrador do sistema os erros mais críticos que ocorrem ocasionalmente com o usuário final.

O Problema

A classe SMTPAppender que é responsável por enviar email no Log4J (apesar de usar internamente as classes da Mail API javax.mail) não dá suporte a envio de emails autenticados, sendo que a maioria dos servidores obriga você a enviar um email utilizando um username e senha existente.

A Solução

A solução é a boa prática da linguagem Orientada a Objeto, isto é, reuso de classes e aprimoramento.
Criando uma nova classe herdada de  SMTPAppender você pode sobrescrever o método activeOptions() para então fornecer o suporte ao envio autenticado.

Segue abaixo um modelo criado por Chris Butler que encontrei em um fórum americano.

Clique aqui e veja o código da classe

Seguindo este modelo é necessário incluir/alterar seu arquivo log4j.properties para as seguintes configurações:

log4j.appender.email=org.apache.log4j.net.AuthSMTPAppender
log4j.appender.email.SMTPHost=<smtp.somedomain.com>
log4j.appender.email.user=<your username>
log4j.appender.email.password=<your password>

Lembrando que para quem utilizar configurações em xml (log4j.xml) os parâmetros a serem alterados e adicionados serão os mesmos, só fazendo a diferença que lá são escritos em tags, por exemplo:

<param name="user" value="danilo" />

Você já precisou enviar dados em um formulário com codificação diferente da sua página?
Como assim? Inicialmente é meio confuso, mas às vezes acontece.
Por exemplo:

No documento principal que está localizado meu <form>, utilizo o conjunto de caracteres UTF-8 (definido na meta tag)
Na página que irá processar o formulário utilizo o conjunto de caracteres iso-8859-1.

Geralmente isso acontece quando você apenas tem o poder de enviar dados, sendo que a ação do formulário é processada por outro site/servidor.

O atributo accept-charset especifica uma lista de conjuntos de caracteres para a entrada de dados que serão aceitas pelo servidor neste formulário.

Então com isso, resolvemos nosso problema.
Um pequeno exemplo:

form.php

1 <html>
2 <head>
3 <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″ />
4 </head>
5
6 <body>
7
8 <form action=”post.php” method=”post” accept-charset=”iso-8859-1″>
9 <input type=”text” name=”nome” value=”Avião faz vôo hoje às 9:30″ />
10 <input type=”submit” value=”Enviar”>
11 </form>
12
13 </body>
14 </html>

post.php

1 <html>
2  <head>
3 <meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1″ />
4 </head>
5
6 <body>
7

8  <?php
echo $_POST['nome'];
 10 ?>
11
12 </body>
13 </html>

Neste exemplo teremos como resposta o texto exatamente na mesma forma que foi escrito, sem nenhum erro de acentuação. Experimente remover o atributo accept-charset e veja o que acontece.

ou também conhecida como “‘Permission denied to set property XULElement.selectedIndex”…

Alguém já sofreu com este problema? Eu já :D
Esse erro do FireFox ocorre quando você tenta dar foco à um campo que possui auto-completar. Isso é muito estranho pois é um ato extremamente normal, essa “exceção” só ocorre em alguns campos e não pode ser capturada via try/catch.

Pesquisando, percebi que realmente se tratava de um bug, ele já foi postado no fórum de bugs da mozilla e já deram como resolvido, estranho pois utilizo a última versão do FF e continuo com esse erro, será mesmo que foi corrigido?

Enfim, como sempre há soluções para contornar o erro… Basta desativar o autocompletar do browser ou do campo específico utilizando o atributo autocomplete=”off”. Exemplo:

1 <input type=”text” name=”campo” autocomplete=”off” />

Infelizmente essa propriedade não é validada pela W3C, então os desenvolvedores costumam setar essa propriedade através de JavaScript.

1 objeto.setAttribute(autocomplete, off);

HTML: Imagem embutida

Outubro 21, 2008

No HTML é possível embutir imagens diretamente pelo código fonte, assim como o Outlook faz para os anexos (imagens) no corpo do email.
Isso não será uma requisição HTTP para o caminho da imagem e sim seu código binário codificado na base64, quer dizer que você pode inserir o código em qualquer página, sem precisar do arquivo físico da imagem.
Essa pode ser considerada uma técnica de otimização, pois a imagem é carregada diretamente com o código fonte HTML, sem requisições externas.

Mas como nem tudo é perfeito, possui algumas restrições: Alguns desenvolvedores não recomendam a utilização de imagens muito grandes para não afetar o desempenho, apesar de rodar perfeitamente. Outra restrição é não funcionar no Internet Explorer (versões 6, 7 até o momento).

[UPDATE]O Internet Explorer 8 já suporta imagens embutidas[/UPDATE]

“Sintaxe” do código: <img src=”data:[mime-type];base64,[imagem-codificada]“>
É necessário inserir a palavra data, o mime-type da imagem, o tipo de codificação e o código binário da imagem codificado na base64. 

Clique aqui e veja o exemplo do código

Caso queira abrir a imagem no Internet Explorer também, a primeira solução que pensei foi forçar o carregamento via JavaScript, isto é, carregar da forma normal (requisitando a URL da imagem). Como já estamos utilizando o atributo SRC, podemos substituir o SRC via JavaScript. O comentário condicional indica que o bloco abaixo só será executado em versões anteriores do Internet Explorer 8.

1 <!–[if lt IE 8]>
2 <script type=”text/javascript”>
3 document.getElementById(‘idImagem’).src = ‘pasta/imagem.jpg’;
4 </script>
5 <![endif]–>

Se alguém tiver uma solução melhor para o IE, por favor postar.

PHP: Comparando arrays

Setembro 22, 2008

Você já tentou comparar dois arrays utilizando os operadores == ou === ?
Em alguns casos esta comparação pode ser útil.

O operador == retorna true caso os dois arrays sejam iguais, isto é deve conter o mesmo número de elementos com as mesmas chaves e valores. Sem importar a ordem de como eles foram atribuídos.

O operador === retorna true caso os dois arrays sejam iguais, isto é deve conter o mesmo número de elementos com as mesmas chaves e valores. Porém, desta vez a ordem de como eles foram atribuídos também devem ser iguais.

Por exemplo:

1 $paises1 = array("brasil", "japao", "italia");
2 $paises2 = array(0 => "brasil", 1 => "japao", 2 => "italia");
3
4 var_dump($paises1 == $paises2); // retorna true

Como podem ver, ele retorna true pois todas as verificações foram atendidas (mesmo número de elementos, chaves iguais e valores iguais).

Agora para uma comparação mais exigente:

1 $paises1 = array("brasil", "japao", "italia");
2 $paises2 = array(1 => "japao", 2 => "italia", 0 => "brasil");
3
4 var_dump($paises1 === $paises2); // retorna false

Já neste caso iria retornar false, mesmo o array sendo igual sua ordem não foi definida da mesma forma.

Sempre tive problemas com sessão na hora de gerar arquivos com a classe FPDF.
Era só colocar o session_start() no ínicio do arquivo que o Internet Explorer não abria mais o PDF, já no Firefox funcionava perfeitamente.
Achando a situação muito estranha, fui tentar achar a solução e como sempre os fóruns americanos me salvaram.

Basta colocar o seguinte comando antes da função session_start(), assim ficando:

1 session_cache_limiter(private);
2 session_start();

Pronto! Agora o Internet Explorer abrirá o PDF normalmente e entederá todas as variáveis de sessão ($_SESSION) normalmente.

[UPDATE]

Lembrando que isso forçará o cache do PDF, caso você não queira deixar o documento no cache é necessário fazer uma chamada diferente da anterior, por exemplo passando um parâmetro dinâmico e único (gerando um número aleatório ou chamando o timestamp são boas soluções).

Exemplo:

document.pdf?token=859859454 
document.pdf?token=238142340
document.pdf?token=132984294 

[/UPDATE]

Validação de CPF

Julho 2, 2008

Eu tenho dó desta pessoa, GLEDE BERNACCI GOLLUSCIO.
Muito provavelmente esta pessoa não consegue se cadastrar na maioria dos sites que exigem CPF, mas por que?
Ele é dono do CPF 111.111.111-11
Muitos sites utilizam uma validação de CPF que restringe essas sequências de números (11111111111, 22222222222, 33333333333, etc) e muita gente não sabe que essas combinações são válidas no algoritmo do CPF.
Mas claro que nem todos estes números ainda têm um dono, porém são válidos.

Alguns outros casos:

Nº do CPF: 444.444.444-44
Nome da Pessoa Física: MARIA FERRO PERON

No do CPF: 888.888.888-88
Nome da Pessoa Física: SEBASTIAO LUIZ BATISTA

Então, tome cuidado ao validar CPF’s em formulários, você pode estar barrando algumas pessoas sem saber.