Gerenciamento de URLs com PHP

Estou devendo aos meus leitores um artigo sobre como criar e gerenciar um bom esquema de URLs usando PHP e freqüentemente sou cobrado por isso.

Neste texto vou tentar explicar o funcionamento e as vantagens do gerenciamento de URLs com PHP em relação ao gerenciamento de URLs usando apenas mod_rewrite.

Este artigo não é uma tradução, mas é fortemente baseado no artigo de Till Quack para o A List Apart, How to succeed with URLs.

A principal vantagem de se usar PHP ao invés de apenas o mod_rewrite é que o PHP é bem mais simples. O mod_rewrite é conhecido tanto pelo seu poder quanto pela sua complexidade, não apenas pelo fato de ser necessário um bom entendimento de expressões regulares para poder usá-lo mas também por ser um tanto obscuro e difícil de entender, principalmente para os iniciantes.

É importante que você entenda o que eu quero dizer com o termo gerenciamento de URLs antes de ler o restante deste texto. Dois textos escritos por mim podem ajudá-lo a entender melhor o que isso significa: Gerenciamento de URLs - Criando URLs amigáveis e URLs amigáveis - esclarecendo dúvidas.

O principal conceito a ser entendido sobre as URLs é que elas nem sempre se referem a um arquivo ou diretório existente no disco. Na realidade, URLs nunca devem se referir a arquivos diretamente. Isso torna suas URLs voláteis, vulneráveis a mudanças. Afinal, como diria Tim Berners-Lee, cool URIs don’t change.

Para usar as técnicas descritas neste texto você precisa do servidor web Apache com o mod_rewrite habilitado, PHP e, principalmente, um bom planejamento. Antes de qualquer coisa, planeje como você quer que suas URLs sejam. Use URLs que resumam de maneira adequada e sucinta o conteúdo do recurso que descrevem.

Bem, agora que você já tem seu esquema de URLs bem planejado, vamos ao que interessa. Mãos à obra.

Crie um script PHP. Vamos chamá-lo de urls.php. Todo tráfego do site (com algumas exceções) será direcionado para esse script. Para fazer isso, crie um arquivo .htaccess na raiz do seu site com o seguinte conteúdo:

RewriteEngine On
RewriteRule !\.(gif|jpg|png|css)$ /raiz_do_site/urls.php

A primeira linha serve para ligar o mod_rewrite e a segunda é a que direciona o tráfego para urls.php. O conteúdo entre parêntesis define as extensões de arquivo que devem ser excluídas desse processo, ou seja, devem ser tratadas da maneira padrão pelo servidor web. Você pode querer incluir mais algumas extensões nessa lista (swf, svg, xml, js, txt, por exemplo), de acordo com a sua necessidade.

Você deve substituir raiz_do_site pelo caminho completo do seu site no servidor, por exemplo, /home/httpd/dominio.com/. Porém, em algumas configurações do apache (todas que eu particularmente já usei), apenas o caminho relativo já é o suficiente, no caso /urls.php ou apenas urls.php.

Agora vamos começar a escrever o script urls.php.

O primeiro passo é verificar se a requisição refere-se a um arquivo existente.

<?php
//Código retirado do A List Apart
$DOCUMENT_ROOT = $_SERVER['DOCUMENT_ROOT'];
$REQUEST_URI = $_SERVER['REQUEST_URI'];
$SCRIPT_FILENAME = $_SERVER['SCRIPT_FILENAME'];
if(file_exists($DOCUMENT_ROOT.$REQUEST_URI)
and ($SCRIPT_FILENAME!=$DOCUMENT_ROOT.$REQUEST_URI)
and ($REQUEST_URI!="/")){
    $url=$REQUEST_URI;
    include($DOCUMENT_ROOT.$url);
    exit();
}

Simplesmente verificamos se a URL refere-se a um arquivo existente, incluimos este arquivo e encerramos o script.

O próximo passo é dividir a URL em pedaços separados por “/” e verificar se o user-agent fez uma requisição pela index do site.

$url=strip_tags($REQUEST_URI);
$url_array=explode("/",$url);
array_shift($url_array); //o primeiro índice sempre será vazio
if(empty($url_array)){
	include("site_index.php");
exit();
}

Trivial, não? Se $REQUEST_URI estiver vazia significa que o usuário está requisitando a index do site. Neste caso, incluimos o script referente à index e encerramos o script.

Bom, se o usuário não requisitou um arquivo existente nem a index do site, chegou a hora de verificar se o que ele está tentando acessar é um conteúdo dinâmico. Provavelmente algo armazenado em um banco de dados, arquivos XML ou o que quer que você use pra armazenar o conteúdo dinâmico do seu site.

Neste caso não há uma fórmula pronta. Cada caso é um caso. Como você já planejou previamente o seu esquema de URLs, com certeza você sabe o que cada URL descreve. Portanto, você deve cuidar dessa parte.

Vou usar uma função fictícia verifica_conteudo() que retorna true caso a URL requisitada corresponda a algum dado presente, por exemplo, no seu banco de dados e uma outra exibe_conteudo() que cuidará para que este conteúdo seja exibido da forma adequada.

if (verifica_conteudo()){
	exibe_conteudo();
	exit();
}

Se também não houver conteúdo dinâmico referente à URL requisitada, só nos resta enviar um erro 404, informando ao user agent que o recurso requisitado não existe.

header("HTTP/1.1 404 Not Found");
exit();

Alguns cuidados devem ser tomados em relação a usuários maliciosos que queiram tentar acessar arquivos do seu sistema que podem causar danos ou garantir acesso privilegiado. Para prevenir esse tipo de coisa, adicione no início do script algumas linhas de código verificando se o usuário está requisitando algo como “../../../algum_script” ou coisas do tipo.

Acessos a URLs longas demais também devem ser prevenidos. Para isso, inclua o seguinte no início do script:

if(strlen($REQUEST_URI)>100){
	header("HTTP/1.1 404 Not Found");
	exit();
}

E é isso. O desenvolvimento desse script vai depender exclusivamente do seu caso específico. A idéia é essa. Se você a entendeu vai ser fácil adaptá-la para a sua necessidade.

Mas, além disso, há outra coisa interessante que se pode fazer com esse script que - para mim pelo menos - não é possível fazer usando apenas regras do mod_rewrite.

Lembra quando você ainda não conhecia as vantagens de se usar URLs amigáveis e usava URLs do tipo index.php?var1=valor1&var2=valor2…? Então, agora você está usando URLs totalmente diferentes e não é legal deixar “quebrados” todos os links e bookmarks que apontam para as URLs antigas, concorda?

Desde que haja alguma correspondência (é bem provável que haja) entre as URLs antigas e as novas, é possível mantê-las funcionando, lançando mão de redirecionamentos permanentes (código HTTP 301).

Como? Bem, a o código vai variar de acordo com o esquema de URLs usado. Você pode criar um código que analise a URL e redirecione para a URL nova. Exemplo: digamos que suas URLs antigas fossem da forma index.php?s=secao&a=artigo e as novas sejam da forma /secao/artigo. Neste caso, ao dividir a URL em pedaços teríamos $url_array[0] = “index.php?s=secao&a=artigo”. O que precisamos fazer é dividí-la novamente em pedaços, pegar os valores das variáveis que nos interessam, manipulá-las se for necessário e redirecionar para a URL correta.

$url = explode("?", $url_array[0]);
array_shift($url);
$url = explode("&", $url[0]);
$secao = explode("=", $url[0]);
$secao = $secao[1];
$artigo = explode("=", $url[1]);
$artigo = $artigo[1];
$url = "http://dominio.com/$secao/$artigo";
header("HTTP/1.1 301 Moved Permanently");
header("Location: " . $url);

Acho que não preciso explicar esse pedaço de código, não é? Bom, de qualquer maneira, você precisa adaptá-lo para o seu caso.

Por último temos que cuidar dos diretórios que eventualmente precisem ser manipulados diretamente pelo servidor web, sem a interferência do mod_rewrite e do nosso script. Por exemplo, você pode ter um diretório “/unsorted/” onde você guarda arquivos aleatórios e deseja que o conteúdo desse diretório seja listado da maneira padrão pelo apache. Ou então, diretórios com arquivos index.html que devem se comportar da maneira padrão. Ou ainda diretórios protegidos por senha.

Neste caso, será necessário adicionar uma regra de reescrita em nosso .htaccess para cada diretório desses. Em princípio eu imaginei que fosse possível colocar todos em uma regra só, utilizando o alternador “|” mas, por algum motivo que desconheço, não funciona. A regra é a seguinte:

RewriteRule   ^unsorted/.*$ - [L]

O “-” informa ao mod_rewrite para sair do caminho e deixar o apache cuidar sozinho da requisição e o “[L]” faz com que essa regra, se satisfeita, seja a última a ser executada. Nenhuma RewriteRule posterior será avaliada. No caso, seu .htaccess deve ficar mais ou menos assim:

RewriteEngine On
RewriteRule   ^unsorted/.*$ - [L]
RewriteRule !\.(gif|jpg|png|css)$ /raiz_do_site/urls.php

Concluindo, mais uma vez deixo claro que não há fórmula mágica para gerenciar suas URLs. Mas o processo não é complicado e, uma vez entendido, é simples aplicá-lo a qualquer caso. Desde que você planeje seu esquema de URLs da maneira adequada.

Leia também:

15 Comentários sobre “Gerenciamento de URLs com PHP”

Faça um comentário

Bruno, é impressão minha ou os endereços dos artigos no feed estão com erro? Aqui ele diz que não encontrou a página.


#2 | Hugo

No meu caso o .htacess teve de ficar assim:

<pre>RewriteEngine on

RewriteBase /

RewriteRule !\.(gif|jpg|png|css)$ index.php</pre>

Se você estiver usando o mod_user do Apache você também terá que usar o RewriteBase, ex:

<pre>RewriteBase /~hugo/</pre>

Eis o resultado (código fonte incluso):

http://meique.luaforge.net/viewsource/


Implementou o detector de resolução né?

Ficou muito mais legal assim, até mais.


Ninguem merece!

Piada de primeiro de abril requentada é uma buesta!

Jonas Galvez, no ano passado, já havia feito esta mesma piada sobre o Internet Explorer deste outro post de vcs…

E fechar os comentários para o post é ridículo… Se bem que foi bem mais inteligente que o TabelaMenos, que "sabotou" o próprio esquema de comentários pra se safar do desmascaramento…

Toscos…


#5 | edir

Um detalhe, nas regras do arquivo .htaccess, se você adicionar a extensão dos arquivo php jutno às demais, isto ja permitirá o acesso à eles da mesma forma que da acesso aos demais indicados gif, jpg, css…

Fazendo isso você elimina a etapa de verificação do arquivo que vem no início da index posta ali.

;)


#6 | Edir

Uma correção, agora que eu vi. Na regra do arquivo .htaccess, tem um erro na expressão. O correto pra funcionar como citei acima é esse.

RewriteRule !.(\.gif|\.jpg|\.png|\.css|\.php|\.htm|\.html)$ /raiz_do_site/urls.php

A expressão agora esta correta pois ele vai verificar se na string de acesso tem no seu término uma das combinações ali. Precisa do "."php para indicar uma extensão senão eu poderia linkar algo como /pagina/php e ele entenderia com um arquivo.

:)


#7 | baidu

Simplesmente , show !

Parabéns !


#8 | Server

Recebo a mensagem de erro:

Parse error: parse error, unexpected $ in /home/user/public_html/urls.php on line 35

O que fazer?


#9 | MAyon Diego Silva ribeiro

Olá! Esse site de vocês é muito 10.
Aprendi muitos codigos diferentes que nunca tinha visto depois que comecei a programar em PHP, essa linguagem fantastica…

Valeu!!!! Vou visitar vocês quando tiver duvidas de novo. Thau…


#10 | Erik

Olá, primeiramente gostaria de agradecer pelo ótimo artigo publicado. Mas estou tendo problemas para implementar essa solução. O que ocorre é o seguinte, em meu site tenho uma conexão com banco de dados orientada a objetos. Então ocorre o erro:

Fatal error: Cannot redeclare class db_sql in /home/localhost/siteX.com/admin/system_files/conexao.php on line 3

Isso ocorre por causa do include no arquivo de gerenciamento de urls no php. Gostaria de saber se já aconteceu este mesmo erro e se há uma solução para este problema. Obrigado.


#11 | everton

estou desenvolvendo um fotolog, e gostaria de saber como ficaria o Rewrite para acessar tipo..
meusite.com.br/everton
e acessar o link meusite.com.br/index.php?u=everton
obrigado


[…] Mas um assunto interessante que apareceu na lista ArqHP, dei uma lida neste artigo, mas ainda tou meio confuso sobre o assunto, vou procurar mais material relacionado e praticar um pouco. Tem uma aplicação em PHP que fiz, que algumas URLs são monstruosas, hehehe, vou ver se dou uma melhorada nessa parte, hehehe. […]


#13 | Antonio Veras

Prezado Bruno,
Gostaria de uma ajuda sua. Uso Location para carregar uma página em PHP em um outro diretório. Neste diretório, se coloco allow from localhost no .htaccess, não previno acesso de fora, ou seja de outra máquina. Na verdade eu queria permitir acesso a um determinado diretório somente se chamadas forem originadas na própria máquina. Como poderia fazer isto?


#14 | jose

amigos,

legal o artigo. como enviar as variaveis para um arquivo 404.php ?

tipo: o usuario tenta acessar o arquivo.php?var1=1&var2=2 que nao existe. logicamente um arquivo error 404 será emitido.
coloquei no .htacess:
ErrorDocument 404 /404.php

so que desejo que este arquivo 404.php receba var1 e var2 para tratar estas varaiveis dentro dele.

como fazer isso?

obrigado.

jose.


Já tinha tido essa idéia antes de direcionar tudo para uma página e lá fazer a distribuição do conteúdo, porém você me ajudou com a idéia da função verifica_conteudo.
Agora o http://www.codesignville.com ficou como eu queria em relação às urls amigáveis.
Valeu.


« O IE é realmente um lixo

IE comendo mosca »

Deixe seu comentário

Buscas populares: Ganhar dinheiro, AdSense, Velox, Acessibilidade, IE7, CSS Position, Quero ganhar dinheiro


Veja as estatísticas