Reescrita de URL com PHP

139

Eu tenho um URL que se parece com:

url.com/picture.php?id=51

Como eu converteria esse URL para:

picture.php/Some-text-goes-here/51

Eu acho que o WordPress faz o mesmo.

Como faço para criar URLs amigáveis ​​em PHP?

Jazerix
fonte
1
Você precisa ajustar muito na configuração dos arquivos Apache ... dê um exemplo do que você realmente precisa? Como você insere este texto? é predefinido ou criado dinamicamente? dar tanta informação quanto possível para obter resposta certa ... e sim url poderia ser um pouco mais descritivo :)
user123_456
1
Por que PHP? O Apache mod_rewriteé tudo o que você precisa para isso e há muitas perguntas sobre isso aqui no SO. E se você "pensa" que o wordpress faz o mesmo, por que você simplesmente não procura ? ;)
nietonfir 5/05
Por que você está fazendo isso? O objetivo é reescrever os URLs; se assim for, use .htaccess ou algo semelhante. Se o objetivo é apenas alterar a string, $ _GET obtém apenas a string de consulta?
Adeneo 5/05
O texto seria o título de uma imagem e, em seguida, o ID após a barra :) Vou ter que analisar o que o mod_rewrite do apache tem a oferecer. Espero que não seja muito difícil: D
Jazerix 05/05

Respostas:

194

Você pode essencialmente fazer isso de duas maneiras:

A rota .htaccess com mod_rewrite

Adicione um arquivo chamado .htaccessna sua pasta raiz e adicione algo como isto:

RewriteEngine on
RewriteRule ^/?Some-text-goes-here/([0-9]+)$ /picture.php?id=$1

Isso instruirá o Apache a ativar o mod_rewrite para esta pasta e, se for solicitado um URL correspondente à expressão regular, ele será reescrito internamente com o que você deseja, sem que o usuário final o veja. Fácil, mas inflexível, portanto, se você precisar de mais energia:

A rota PHP

Coloque o seguinte em seu .htaccess: (observe a barra inicial)

FallbackResource /index.php

Isso solicitará que você execute o index.phparquivo para todos os arquivos que normalmente não podem ser encontrados no seu site. Lá você pode, por exemplo:

$path = ltrim($_SERVER['REQUEST_URI'], '/');    // Trim leading slash(es)
$elements = explode('/', $path);                // Split path on slashes
if(empty($elements[0])) {                       // No path elements means home
    ShowHomepage();
} else switch(array_shift($elements))             // Pop off first item and switch
{
    case 'Some-text-goes-here':
        ShowPicture($elements); // passes rest of parameters to internal function
        break;
    case 'more':
        ...
    default:
        header('HTTP/1.1 404 Not Found');
        Show404Error();
}

É assim que sites grandes e sistemas CMS o fazem, porque permite muito mais flexibilidade na análise de URLs, URLs dependentes de configuração e banco de dados, etc. Para uso esporádico, as regras de reescrita codificadas permanecem .htaccessbem.

Niels Keurentjes
fonte
Em vez de codificá-lo, você pode usar o regex para ignorar completamente a string. Em outras palavras, a única coisa que conta é a parte do ID. Ir para picture.php/invalid-text/51também redirecionaria para o mesmo local. Você também pode adicionar uma verificação para ver se a sequência está correta e, se não, redirecionar novamente para o local correto. Foi assim que fiz em um dos meus sites usando o htaccess.
Mike
7
Conveniente para sites menores, mas não é realmente prático se você precisar analisar /blog/25tanto quanto /picture/51e /download/684. Além disso, é considerado uma prática muito ruim (e você fica Google PR penalizado!) Se não todos URLs gerados aleatoriamente retornar corretamente 404.
Niels Keurentjes
5
no meu sistema, pelo menos, que era FallbackResource /index.php(note a barra à esquerda)
Jack James
4
@olli: o comentário de má prática refere-se especificamente a "não retornar 404 para URLs inexistentes", resolvido pela solução na própria resposta. Quanto à primeira pergunta - FallbackResourceapenas entra em ação arquivos que realmente não existem no sistema de arquivos, daí o fallback . Portanto, se você tiver um arquivo /static/styles.csse se referir a ele como http://mydomain.tld/static/styles.csso código nunca é executado, faça-o funcionar conforme o esperado e com a intenção transparente.
Niels Keurentjes
você deve usar if($elements[0] === NULL), pois $elementsainda retornará uma contagem de um, mesmo que esteja vazio.
Fireinspace
57

Se você quiser apenas alterar a rota para picture.phpadicionar regras de reescrita .htaccess, atenderá às suas necessidades, mas, se desejar reescrever a URL como no Wordpress, o PHP é o caminho. Aqui está um exemplo simples para começar.

Estrutura de pastas

Existem dois arquivos que são necessários na pasta raiz .htaccesse index.php, e seria bom colocar o restante dos .phparquivos em uma pasta separada, como inc/.

root/
  inc/
  .htaccess
  index.php

.htaccess

RewriteEngine On
RewriteRule ^inc/.*$ index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]

Este arquivo possui quatro diretivas:

  1. RewriteEngine - ativar o mecanismo de reescrita
  2. RewriteRule- negar acesso a todos os arquivos da inc/pasta, redirecionar qualquer chamada para essa pasta paraindex.php
  3. RewriteCond - permite acesso direto a todos os outros arquivos (como imagens, css ou scripts)
  4. RewriteRule - redirecione qualquer outra coisa para index.php

index.php

Como agora tudo é redirecionado para index.php, será determinado se o URL está correto, todos os parâmetros estão presentes e se o tipo de parâmetro está correto.

Para testar o URL, precisamos ter um conjunto de regras e a melhor ferramenta para isso é uma expressão regular. Usando expressões regulares, mataremos duas moscas com um golpe. Url, para passar neste teste deve ter todos os parâmetros necessários testados em caracteres permitidos. Aqui estão alguns exemplos de regras.

$rules = array( 
    'picture'   => "/picture/(?'text'[^/]+)/(?'id'\d+)",    // '/picture/some-text/51'
    'album'     => "/album/(?'album'[\w\-]+)",              // '/album/album-slug'
    'category'  => "/category/(?'category'[\w\-]+)",        // '/category/category-slug'
    'page'      => "/page/(?'page'about|contact)",          // '/page/about', '/page/contact'
    'post'      => "/(?'post'[\w\-]+)",                     // '/post-slug'
    'home'      => "/"                                      // '/'
);

O próximo passo é preparar a solicitação uri.

$uri = rtrim( dirname($_SERVER["SCRIPT_NAME"]), '/' );
$uri = '/' . trim( str_replace( $uri, '', $_SERVER['REQUEST_URI'] ), '/' );
$uri = urldecode( $uri );

Agora que temos o pedido uri, a etapa final é testar o uri nas regras de expressão regular.

foreach ( $rules as $action => $rule ) {
    if ( preg_match( '~^'.$rule.'$~i', $uri, $params ) ) {
        /* now you know the action and parameters so you can 
         * include appropriate template file ( or proceed in some other way )
         */
    }
}

Uma correspondência bem-sucedida, como usamos subpadrões nomeados em regex, preencherá a $paramsmatriz quase da mesma forma que o PHP preenche a $_GETmatriz. No entanto, ao usar um URL dinâmico, a $_GETmatriz é preenchida sem nenhuma verificação dos parâmetros.

    / foto / alguns + texto / 51

    Matriz
    (
        [0] => / imagem / algum texto / 51
        [text] => algum texto
        [1] => algum texto
        [id] => 51
        [2] => 51
    )

    picture.php? text = some + text & id = 51

    Matriz
    (
        [text] => algum texto
        [id] => 51
    )

Essas poucas linhas de código e um conhecimento básico de expressões regulares são suficientes para começar a construir um sistema de roteamento sólido.

Fonte completa

define( 'INCLUDE_DIR', dirname( __FILE__ ) . '/inc/' );

$rules = array( 
    'picture'   => "/picture/(?'text'[^/]+)/(?'id'\d+)",    // '/picture/some-text/51'
    'album'     => "/album/(?'album'[\w\-]+)",              // '/album/album-slug'
    'category'  => "/category/(?'category'[\w\-]+)",        // '/category/category-slug'
    'page'      => "/page/(?'page'about|contact)",          // '/page/about', '/page/contact'
    'post'      => "/(?'post'[\w\-]+)",                     // '/post-slug'
    'home'      => "/"                                      // '/'
);

$uri = rtrim( dirname($_SERVER["SCRIPT_NAME"]), '/' );
$uri = '/' . trim( str_replace( $uri, '', $_SERVER['REQUEST_URI'] ), '/' );
$uri = urldecode( $uri );

foreach ( $rules as $action => $rule ) {
    if ( preg_match( '~^'.$rule.'$~i', $uri, $params ) ) {
        /* now you know the action and parameters so you can 
         * include appropriate template file ( or proceed in some other way )
         */
        include( INCLUDE_DIR . $action . '.php' );

        // exit to avoid the 404 message 
        exit();
    }
}

// nothing is found so handle the 404 error
include( INCLUDE_DIR . '404.php' );
Danijel
fonte
2
Como você lê os parâmetros? Ele não funciona com $ post_id = htmlentities ($ _ GET ['post']);
andrebruton
@ Danijel Posso ter um código fonte completo? Eu tentei o código acima, mas apenas o texto foi produzido, CSS sem efeito. Obrigado.
4 Deixar a capa
6

este é um arquivo .htaccess que encaminha quase tudo para index.php

# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_URI} !-l
RewriteCond %{REQUEST_FILENAME} !\.(ico|css|png|jpg|gif|js)$ [NC]
# otherwise forward it to index.php
RewriteRule . index.php

depende de você analisar $ _SERVER ["REQUEST_URI"] e encaminhar para picture.php ou qualquer outra coisa

Luca Rocchi
fonte
8
O Apache introduziu a FallbackResourcediretiva algumas versões principais atrás, que agora é a maneira preferida de implementar esse comportamento a um custo de desempenho mais baixo, pois não precisa iniciar todo o mecanismo de reescrita. Documentação aqui . Suas regras também são falhas porque você não faz referência a diretórios ( !-d) e todos os filtros de extensão são obsoletos - -feles já devem ser capturados.
Niels Keurentjes
5

PHP não é o que você está procurando, confira mod_rewrite

rauchmelder
fonte
isso afetará todos os URLs?
Jazerix 5/05
@ Jazerix Isso depende das regras que você define.
Nietonfir 5/05
2

Embora já tenha respondido, e a intenção do autor é criar um aplicativo do tipo front controller, mas estou postando uma regra literal para o problema solicitado. se alguém com o mesmo problema.

RewriteEngine On
RewriteRule ^([^/]+)/([^/]+)/([\d]+)$ $1?id=$3 [L]

Acima deve funcionar para o URL picture.php/Some-text-goes-here/51. sem usar o index.php como um aplicativo de redirecionamento.

Abhishek Gurjar
fonte
Preciso saber qual URL temos que escrever no href? porque estou recebendo 404
questionbank