Caching em PHP com uma técnica “obscura” mas muito eficiente
Posted: December 25th, 2008 | Author: Felipe Ribeiro | Filed under: apache, php, web 2.0 | Tags: apache, php, web 2.0 | 14 Comments »Caching é fundamental para a escalabilidade de aplicações Web. Existem diversas ferramentas que oferecem diferentes maneiras de se fazer isso, seja no Smarty, no APC, nos diversos frameworks, ou Memcached para os mais drásticos…
Mas essa é a “maneira Rasmus Lerdorf” de se fazer cache com PHP sem nenhuma ferramenta externa e com uma sacada fenomenal.
Para um servidor Web é muito mais rápido servir arquivos estáticos do que esperar que aquele arquivo seja interpretado por algum módulo ou que seja executado em CGI e é isso que esse método faz, gera arquivos estáticos sob demanda, utilizando os recursos que o Apache oferece.
Agora imagine que você tem um site de notícias e quer que elas sejam tratadas como arquivos estáticos, e os links para cada notícia seria algo como:
http://meusite.com.br/noticias/000001.html
1º passo: Setamos nas configurações do Apache para que a página de erro 404 seja um arquivo .php (isso pode ser feito no .htaccess da pasta ou nas configurações do Apache propriamente dito). No caso do .htaccess, basta colocar isso:
ErrorDocument 404 /noticias/gera_cache.php
2º passo: Criamos o arquivo gera_cache.php, que irá tratar as requisições que teriam como resposta o erro 404 (Not Found) com o seguinte codigo:
<?php
$id = basename($_SERVER['REDIRECT_URL'], '.html');
/* Acessa a página dinâmica */
$html = file_get_contents(sprintf("http://meusite.com.br/noticias.php?id=%d",$id));
/* O ideal é fazer algum tratamento de erros, para evitar a
criação de arquivos para ids inválidos */
/* Exibe o conteúdo */
header(sprintf('%s 200', $_SERVER['SERVER_PROTOCOL']));
echo $html;
/* Salva o conteúdo em um arquivo .html */
$fp = fopen(sprintf(dirname(__FILE__)."/%d.html", $id), "w");
fputs($fp, $html);
fclose($fp);
?>
Sendo assim, o que vai acontecer:
Quando o usuário acessar pela primeira vez o link http://meusite.com.br/noticias/000001.html, o arquivo /noticias/000001.html não existirá e o usuário será redirecionado para o gera_cache.php. O gera_cache.php acessa a página dinâmica que exibe o conteúdo da página com o id passado (000001) e salva em um html. Nos acessos consecutivos ao http://meusite.com.br/noticias/000001.html o arquivo existirá e não passará mais pelo PHP.
É uma técnica bem “tricky” e que precisa de cuidados, por exemplo, você precisa ter uma rotina que expira os arquivos em cache após algum tempo e quando houver alguma alteração em determinada informação que interfere na página que está em cache, para garantir consistência dos dados. Uma maneira muito simples de se fazer isso é usando a função filectime do PHP para checar a idade dos arquivos em uma rotina que rodaria em background e apagaria os que fossem mais velhos que o tempo desejado. Mas funciona muito bem!
P.S.: Um artigo que li dizia que apesar dessa técnica ter se tornado pública através do Rasmus, ela foi criada mesmo pelo Stig Bakken













Salve,
Muito boa essa técnica, é quase um url rewrite com cache.
O cara passa a url site.com.br/produto-00001.html, ai cai no 404 que gera o arquivo nesse formato do caching.
Abraços.
Ola Felipe!
É uma técnica bem interessante em seu conceito, mas pode gerar MUITA dor de cabeça da forma que foi feita. Alguns pontos:
1 – Todo retorno de 404 irá gerar um html, mesmo que seja em branco, certo? Eu poderia fazer um script que ficaria acessando urls aleatorias no seu site, gerando assim milhares de arquivos.
2 – Para SEO isso também complica as coisas. Primeiro que o Google não indexa qnd houver o 404, segundo que você pode gerar conteúdo pra qualquer url.
3 – A manutenção disso seria um pouco complicada, apagar os arquivos depois de uma certa idade até pode ser válida, mas não seria a ideal.
Irei tentar melhorar esse sistema e postar em meu blog, podemos gerar um debate para chegar em uma versão segura e eficiente dessa técnica. Que tal?
[]s e parabéns pelo blog.
Olá aissegoo!
Vou responder teus questionamentos:
1 – Sim, esse script que tá demonstrado aí é apenas conceitual, não incluí nele tratamento de erro, mas certamente é necessário ser feito no ambiente de produção.
2 – Exato, o Google não indexaria caso ele fosse o primeiro a atingir aquela URL, caso não seja o primeiro, indexará normalmente. Caso ele tenha sido o primeiro, quando o bot chegar na tal url, o arquivo será gerado, aí na próxima passagem dele, provavelmente daqui a uma semana, aí sim indexará. Mas tudo isso pode ser evitado com a linha que adicionei agora de header.
Não entendi o que você quis dizer com “segundo que você pode gerar conteúdo pra qualquer url.”
3 – Mas é isso que o Smarty::is_cached() faz… ele checa se o arquivo existe e se a idade dele é menor que o Smarty::cache_lifetime, se for ele apaga para gerar de novo. A diferença é que no caso como o do Smarty as coisas são feitas sob demanda, e nesse caso pode-se dizer que seria um processamento “em lote”.
Obrigado pelo comentário e sugiro esse artigo para leitura:
http://phpadvent.org/2008/php-without-php-by-terry-chay
Um grande abraço
Ola Felipe!
Ah sim, a idéia do script é uma boa, fazemos muito isso onde trabalho pois temos sites que tem muitos acessos e se for deixar tudo no banco de dados, haja servidores! Voltando a falar do sistema em questão, considerando alguns pontos novamente:
1) Pode ser que somente o Google acesse uma certa url, aí ele recebe 404, o arquivo html é gerado, depois é apagado e novamente o Google volta depois de um tempo e ache o 404 novamente. A frequencia com que o crawler visita as urls não é exatamente definida.
2) Uma das coisas que considero mais grave é que qualquer id digitada, ele gera um html, mesmo com conteúdo vazio. Alguem com má intenção poderia forçar o nosso site a ter qualquer tipo de conteúdo, pelo menos o arquivo e com o nome na url. Isso abre uma brecha para alguém atacar com SEO o site.
3) Embora o tamanho do arquivo seja mínimo, ainda sim pode-se fazer um sistema que gere infinitos arquivos dentro do nosso site. Isso torna-se uma brecha de segurança.
A parte de apagar os arquivos depois de uma certa idade eu viajei em dizer que seria ruim, seria mais apropriado dizer que existem outros meios para se atualizar o conteúdo. Mesmo porque uso dessa técnica em várias aplicações e ela funciona muito bem em vários casos.
O conceito do sistema é muito boa, já vi algumas variações disso e funcionam muito bem. O usuário só deve se alertar que precisa usar isso com certos cuidados, aperfeiçoar bem a idéia para não ter dores de cabeça no futuro.
Desculpe a redação hehehe, mas é importante trocar essas experiências.
[]s
Como eu te disse, essa linha de header resolve o problema do 404 para o Google, e o tratamento de erro pode ser feito para tratar ids inexistentes.
Realmente, uma técnica muito obscura. Mas não entendi realmente o problema de se ter uma página 404 estática padrão, aonde os browsers que se possuem JavaScript alterem o conteúdo de determinado elemento para o nome da página procurada em questão, do contrário fica somente a página 404 convencional sem nome alterado.
Ficaria algo como:
ErrorDocument 404 /noticias/404_estatico.xhtml e o Apache geraria o log para estatísticas.
Estou louco?
Opa cara,
Acho que você não entendeu bem a sacada. Inclusive é nesse esquema que o site php.net funciona.
Vamos supor que você quer acessar a notícia de id 42, o link vai estar apontando para:
http://meusite.com.br/noticias/42.html
Quando você bater nesse endereço pela primeira vez, ele nao vai existir, então o que vai acontecer: O PHP vai ser chamado para gerar essa página. Ele vai carregar a pagina dinâmica de notícias com o id 42 e salvar como um arquivo estático html com o nome 42.html.
Nos próximos hits a essa página, o arquivo 42.html vai existir e o PHP não precisará ser chamado, o que torna o sistema MUITO mais rápido.
Não tem muito a ver com log e páginas de erro estáticas vs. dinâmicas não. É só uma maneira de fazer cache de maneira a aproveitar o que o ambiente proporciona. E se o ID que você quer não existir, você vai pegar no tratamento de erro e mandar a página de erro 404 mesmo.
Abraço
[...] Você pode conferí-la direto no site do cara: Caching em PHP com uma técnica “obscura” mas muito eficiente [...]
Felipe, muito bo o artigo. Testei mas $_SERVER['REDIRECT_URL'] nao funcionou e o meu PHP nao recupera esta variavel. Sera alguma configuracao do Apache?
Acho que você deveria olhar o RewriteCond com a opção “!-f”, que vai fazer um redirecionamento quando o arquivo não existe. Usar o 404 para isso não é elegante nem correto.
Valeu pelo comentário!
Realmente é uma solução mais elegante. Mas não sei se podemos
considerar o tratamento do 404 como “incorreto”, afinal o erro 404 é o
código que também identifica a inexistência do arquivo. São duas
maneiras de se dizer a mesma coisa, porém a sua é sim mais elegante.
[]’s
Idéia bem interessante essa idéia do erro 404!
Felipe, uma idéia seria criar a pagina html no momento do insert no banco de dados, e criá-la novamente em caso de update.
O que acha?
[]’s
Também penso como o Fabio, acho muito melhor criar a página estatica no momento do “INSERT” no banco de dados.