Reescritura de URL con PHP

139

Tengo una URL que se parece a:

url.com/picture.php?id=51

¿Cómo haría para convertir esa URL a:

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

Creo que WordPress hace lo mismo.

¿Cómo hago para crear URL amigables en PHP?

Jazerix
fuente
1
Necesita modificar mucho la configuración de los archivos de Apache ... ¿dar un ejemplo de lo que realmente necesita? ¿Cómo se inserta este texto? ¿Está predefinido o se creó dinámicamente? dar tanta información como sea posible para obtener una respuesta correcta ... y url sí podría ser un poco más descriptivo :)
user123_456
1
¿Por qué PHP? Apache mod_rewritees todo lo que necesita para esto y hay muchas preguntas al respecto aquí en SO. Y si "piensas" que Wordpress hace lo mismo, ¿por qué no lo buscas ? ;)
nietonfir
¿Por qué estás haciendo esto? ¿El objetivo es reescribir las URL? Si es así, use .htaccess o algo similar. Si el objetivo es solo cambiar la cadena, $ _GET solo obtiene la cadena de consulta.
adeneo
El texto sería el título de una imagen y luego la identificación después de la barra inclinada :) Tendré que investigar qué ofrece mod_rewrite de apache. Espero que no sea demasiado difícil: D
Jazerix

Respuestas:

194

Básicamente puede hacer esto de 2 maneras:

La ruta .htaccess con mod_rewrite

Agregue un archivo llamado .htaccessen su carpeta raíz y agregue algo como esto:

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

Esto le indicará a Apache que habilite mod_rewrite para esta carpeta, y si se le solicita una URL que coincida con la expresión regular, la reescribe internamente a lo que desea, sin que el usuario final la vea. Fácil, pero inflexible, así que si necesitas más potencia:

La ruta PHP

Ponga lo siguiente en su .htaccess en su lugar: (tenga en cuenta la barra inclinada)

FallbackResource /index.php

Esto le indicará que ejecute su index.phppara todos los archivos que normalmente no puede encontrar en su sitio. Ahí puedes, por ejemplo:

$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();
}

Así es como lo hacen los sitios grandes y los sistemas CMS, porque permite mucha más flexibilidad al analizar URL, URL dependientes de configuración y base de datos, etc. .htaccessSin embargo, para un uso esporádico las reglas de reescritura codificadas funcionarán bien.

Niels Keurentjes
fuente
En lugar de codificarlo, podría usar regex para ignorar la cadena por completo. En otras palabras, lo único que cuenta es la parte de ID. Ir a picture.php/invalid-text/51también redirigiría a la misma ubicación. También puede agregar una verificación para ver si la cadena es correcta y, de lo contrario, redirigir nuevamente a la ubicación correcta. Así es como lo hice en uno de mis sitios usando htaccess.
Mike
77
Conveniente para sitios más pequeños, pero no realmente práctico si tiene que analizar /blog/25, así como /picture/51y /download/684. Además, se considera una práctica muy mala (¡y hace que se penalice a Google PR!) Si no todas las URL generadas al azar devuelven correctamente 404.
Niels Keurentjes
55
al menos en mi sistema, eso fue FallbackResource /index.php(tenga en cuenta la barra diagonal)
Jack James
44
@olli: el comentario de mala práctica se refiere específicamente a "no devolver 404's por URL inexistentes", que se resuelve con la solución en la respuesta misma. En cuanto a la primera pregunta, FallbackResourcesolo se activan los archivos que en realidad no existen en el sistema de archivos, de ahí el retroceso . Entonces, si tiene un archivo /static/styles.cssy se refiere a él, ya que http://mydomain.tld/static/styles.cssel código nunca se ejecuta, lo hace funcionar como se esperaba y de manera transparente.
Niels Keurentjes
en su if($elements[0] === NULL)lugar, debe usarlo , ya $elementsque aún devolverá un recuento de uno, incluso si está vacío.
fireinspace
57

Si solo desea cambiar la ruta para picture.phpluego agregar la regla de reescritura .htaccessservirá a sus necesidades, pero, si desea la reescritura de URL como en Wordpress, entonces PHP es el camino. Aquí hay un ejemplo simple para comenzar.

Estructura de carpetas

Hay dos archivos que se necesitan en la carpeta raíz, .htaccessy index.phpsería bueno colocar el resto de los .phparchivos en una carpeta 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 archivo tiene cuatro directivas:

  1. RewriteEngine - habilitar el motor de reescritura
  2. RewriteRule- denegar el acceso a todos los archivos en la inc/carpeta, redirigir cualquier llamada a esa carpeta aindex.php
  3. RewriteCond - Permitir acceso directo a todos los demás archivos (como imágenes, CSS o scripts)
  4. RewriteRule - redirigir cualquier otra cosa a index.php

index.php

Como ahora todo se redirige a index.php, se determinará si la url es correcta, si todos los parámetros están presentes y si el tipo de parámetros es correcto.

Para probar la url necesitamos tener un conjunto de reglas, y la mejor herramienta para eso es una expresión regular. Al usar expresiones regulares mataremos dos moscas de un solo golpe. Url, para pasar esta prueba debe tener todos los parámetros necesarios que se prueban en los caracteres permitidos. Aquí hay algunos ejemplos de reglas.

$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'      => "/"                                      // '/'
);

Lo siguiente es preparar la solicitud uri.

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

Ahora que tenemos la solicitud uri, el paso final es probar uri en las reglas de expresión 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 )
         */
    }
}

La coincidencia exitosa, ya que usamos subpatrones con nombre en expresiones regulares, llenará la $paramsmatriz casi de la misma manera que PHP llena la $_GETmatriz. Sin embargo, cuando se utiliza una url dinámica, la $_GETmatriz se llena sin ninguna verificación de los parámetros.

    / picture / some + text / 51

    Formación
    (
        [0] => / imagen / algún texto / 51
        [texto] => algún texto
        [1] => algún texto
        [id] => 51
        [2] => 51
    )

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

    Formación
    (
        [texto] => algún texto
        [id] => 51
    )

Estas pocas líneas de código y un conocimiento básico de las expresiones regulares son suficientes para comenzar a construir un sistema de enrutamiento sólido.

Fuente 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
fuente
2
¿Cómo se leen los parámetros? No funciona con $ post_id = htmlentities ($ _ GET ['post']);
andrebruton
@Danijel ¿Puedo tener un código fuente completo? Probé el código anterior pero solo se generó texto, CSS sin efecto. Gracias.
4 Deja la cubierta el
6

Este es un archivo .htaccess que reenvía casi todo a 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

entonces depende de usted analizar $ _SERVER ["REQUEST_URI"] y enrutar a picture.php o lo que sea

Luca Rocchi
fuente
8
Apache introdujo el FallbackResource directiva hace algunas versiones importantes, que ahora es la forma preferida de implementar este comportamiento a un costo de rendimiento más bajo, ya que no necesita lanzar todo el motor de reescritura. Documentación aquí . Sus reglas también son defectuosas porque no hace referencia a los directorios ( !-d) y todos los filtros de extensión son obsoletos, -fya deberían ser detectados.
Niels Keurentjes 05 de
5

PHP no es lo que estás buscando, mira mod_rewrite

rauchmelder
fuente
¿afectará a todas las URL?
Jazerix
@Jazerix Esto depende de las reglas que defina.
nietonfir
2

Aunque ya se respondió, y la intención del autor es crear una aplicación de tipo controlador frontal, pero estoy publicando una regla literal para el problema solicitado. si alguien tiene el problema por lo mismo.

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

Lo anterior debería funcionar para url picture.php/Some-text-goes-here/51 . sin usar index.php como aplicación de redireccionamiento.

Abhishek Gurjar
fuente
¿Necesito saber qué URL tenemos que escribir en href? porque estoy obteniendo 404
questionbank